New upstream snapshot 2.2~pre20180302+dfsg1 2.2%pre20180302+dfsg1
authormirabilos <tg@debian.org>
Mon, 5 Mar 2018 02:47:00 +0000 (03:47 +0100)
committermirabilos <tg@debian.org>
Mon, 5 Mar 2018 02:48:05 +0000 (03:48 +0100)
(also with new excludes)

55 files changed:
CMakeLists.txt
fluid/voice.cpp
fluid/voice.h
fonts/mscore/mscore.sfd
libmscore/chord.cpp
libmscore/cmd.cpp
libmscore/edit.cpp
libmscore/note.cpp
libmscore/repeatlist.cpp
libmscore/score.cpp
mscore/CMakeLists.txt
mscore/album.cpp
mscore/exportxml.cpp
mscore/importgtp-gp6.cpp
mscore/importmxmlnoteduration.cpp [new file with mode: 0644]
mscore/importmxmlnoteduration.h [new file with mode: 0644]
mscore/importmxmlnotepitch.cpp [new file with mode: 0644]
mscore/importmxmlnotepitch.h [new file with mode: 0644]
mscore/importmxmlpass1.cpp
mscore/importmxmlpass1.h
mscore/importmxmlpass2.cpp
mscore/importmxmlpass2.h
mscore/instrwidget.cpp
mscore/macos/cocoabridge.h [new file with mode: 0644]
mscore/macos/cocoabridge.mm [new file with mode: 0644]
mscore/measureproperties.ui
mscore/musescore.cpp
mscore/musicxmlsupport.cpp
mscore/musicxmlsupport.h
mscore/paintengine_p.h [deleted file]
mscore/pianotools.cpp
mscore/pianotools.h
mscore/pluginManager.ui
mscore/revision.h
mscore/scoreview.cpp
mscore/shortcut.cpp
mscore/stringutils.cpp [new file with mode: 0644]
mscore/stringutils.h [new file with mode: 0644]
mscore/svggenerator.cpp
mscore/textcursor.cpp
mscore/tupletdialog.cpp
mtest/CMakeLists.txt
mtest/libmscore/repeat/repeat47.mscx [new file with mode: 0644]
mtest/libmscore/repeat/tst_repeat.cpp
mtest/musicxml/io/testDoubleClefError.xml [new file with mode: 0644]
mtest/musicxml/io/testDoubleClefError_ref.xml [new file with mode: 0644]
mtest/musicxml/io/tst_mxml_io.cpp
mtest/stringutils/CMakeLists.txt [new file with mode: 0644]
mtest/stringutils/tst_stringutils.cpp [new file with mode: 0644]
share/instruments/instruments.xml
share/instruments/instrumentsxml.h
share/sound/CMakeLists.txt [deleted file]
share/sound/FluidR3MonoChangeLog.txt [deleted file]
share/sound/FluidR3Mono_GM.sf3 [deleted file]
share/sound/FluidR3Mono_License.md [deleted file]

index 3f54911..853c149 100644 (file)
@@ -153,7 +153,8 @@ if (APPLE)
       find_library(CoreMidiFW            NAMES CoreMIDI)
       find_library(SystemConfigurationFW NAMES SystemConfiguration)
       find_library(CoreServicesFW        NAMES CoreServices)
-      set(OsxFrameworks ${AudioToolboxFW} ${AudioUnitFW} ${CoreAudioFW} ${CoreMidiFW} ${SystemConfigurationFW} ${CoreServicesFW})
+      find_library(AppKit                NAMES AppKit)
+      set(OsxFrameworks ${AudioToolboxFW} ${AudioUnitFW} ${CoreAudioFW} ${CoreMidiFW} ${SystemConfigurationFW} ${CoreServicesFW} ${AppKit})
 endif (APPLE)
 
 #
@@ -221,10 +222,7 @@ endif (MINGW OR APPLE)
 
 ##
 ##  look for Qt5
-##  5.3 can be used, it should be compatible if you don't have access to 5.4.
-##  5.4 is used on windows and  mac. It's better to use it on other platforms
-##  if you can.
-SET(QT_MIN_VERSION    "5.3.0")
+SET(QT_MIN_VERSION    "5.4.0")
 
 SET(QT_USE_QTXML         TRUE)
 SET(QT_USE_QTSVG         TRUE)
index f91a737..292cbb3 100644 (file)
@@ -300,11 +300,13 @@ void Voice::write(unsigned n, float* out, float* reverb, float* chorus)
 
       /******************* mod env **********************/
 
-      // if we wouldn't calculate sample accurate volume
-      // we wouldn't advance beyond FLUID_VOICE_ENVDELAY
-      // and effectively always skip the first buffer rendering thus adding n to modenv_count
-      if (volenv_section > FLUID_VOICE_ENVDELAY)
-            modenv_count += n;
+      /* Skip to decay phase if delay and attack envelope sections each are
+       * less than 100 samples long. This avoids popping noises due to the
+       * mod envelope being out-of-sync with the sample-based volume envelope. */
+      if (modenv_section < 2 && modenv_data[FLUID_VOICE_ENVDELAY].count < 100 && modenv_data[FLUID_VOICE_ENVATTACK].count < 100) {
+            modenv_section = 2;
+            modenv_val     = 1.0f;
+            }
 
       env_data = &modenv_data[modenv_section];
 
@@ -502,7 +504,7 @@ void Voice::write(unsigned n, float* out, float* reverb, float* chorus)
       float cent = pitch + modlfo_val * modlfo_to_pitch
                    + viblfo_val * viblfo_to_pitch
                    + modenv_val * modenv_to_pitch;
-      phase_incr = _fluid->ct2hz_real(cent) / root_pitch;
+      phase_incr = _fluid->ct2hz_real(cent) / root_pitch_hz;
       }
 
       /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */
@@ -747,17 +749,6 @@ void Voice::voice_start()
             dest_gen->mod      += modval;
             }
 
-     /* The GEN_PITCH is a hack to fit the pitch bend controller into the
-      * modulator paradigm.  Now the nominal pitch of the key is set.
-      * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a
-      * non-realtime parameter. So we don't allow modulation (as opposed
-      * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied,
-      * one key remains fixed. Here C3 (MIDI number 60) is used.
-      */
-
-      gen[GEN_PITCH].val = _noteTuning + _fluid->getPitch(60) + (gen[GEN_SCALETUNE].val * .01 *
-               (_fluid->getPitch(key) - _fluid->getPitch(60)));
-
      /* Now the generators are initialized, nominal and modulation value.
       * The voice parameters (which depend on generators) are calculated
       * with update_param. Processing the list of generator
@@ -794,6 +785,30 @@ void Voice::voice_start()
       status = FLUID_VOICE_ON;
       }
 
+//---------------------------------------------------------
+//   fluid_voice_calculate_gen_pitch
+//---------------------------------------------------------
+
+void Voice::calculate_gen_pitch()
+      {
+      float x;
+     /* The GEN_PITCH is a hack to fit the pitch bend controller into the
+      * modulator paradigm.  Now the nominal pitch of the key is set.
+      * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a
+      * non-realtime parameter. So we don't allow modulation (as opposed
+      * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied,
+      * one key remains fixed. Here C3 (MIDI number 60) is used.
+      */
+      //if (channel->tuning != 0) {
+            /* pitch(scalekey) + scale * (pitch(key) - pitch(scalekey)) */
+      x = _fluid->getPitch((int)(root_pitch / 100.0f));
+      gen[GEN_PITCH].val = _noteTuning + (x + (gen[GEN_SCALETUNE].val / 100.0f * (_fluid->getPitch(key) - x)));
+      //      }
+      //else {
+      //      gen[GEN_PITCH].val = _noteTuning + (gen[GEN_SCALETUNE].val * (key - root_pitch / 100.0f) + root_pitch);
+      //      }
+      }
+
 /*
  * calculate_hold_decay_frames
  */
@@ -925,9 +940,11 @@ void Voice::update_param(int _gen)
                   else {
                         root_pitch = sample->origpitch * 100.0f - sample->pitchadj;
                         }
-                  root_pitch = _fluid->ct2hz(root_pitch);
+                  root_pitch_hz = _fluid->ct2hz(root_pitch);
                   if (sample != 0)
-                        root_pitch *= (float) _fluid->sample_rate / sample->samplerate;
+                        root_pitch_hz *= (float) _fluid->sample_rate / sample->samplerate;
+                  /* voice pitch depends on voice root_pitch, so calculate voice pitch now */
+                  calculate_gen_pitch();
                   break;
 
             case GEN_FILTERFC:
index b45edc6..543b6ff 100644 (file)
@@ -104,7 +104,7 @@ class Voice
        float attenuation;        /* the attenuation in centibels */
        float min_attenuation_cB; /* Estimate on the smallest possible attenuation
                                                  * during the lifetime of the voice */
-       float root_pitch;
+       float root_pitch, root_pitch_hz;
 
        /* sample and loop start and end points (offset in sample memory).  */
        int start;
@@ -219,6 +219,7 @@ class Voice
       void noteoff();
       void kill_excl();
       int calculate_hold_decay_frames(int gen_base, int gen_key2base, int is_decay);
+      void calculate_gen_pitch();
 
       /* A voice is 'ON', if it has not yet received a noteoff
        * event. Sending a noteoff event will advance the envelopes to
index 5c944b3..f1c21c6 100644 (file)
@@ -3,8 +3,8 @@ FontName: MScore
 FullName: MScore for scores
 FamilyName: MScore
 Weight: Regular
-Copyright: 
-Version: 
+Copyright: This font is distributed under the GNU General Public License. As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License.
+Version:
 DefaultBaseFilename: mscore
 ItalicAngle: 0
 UnderlinePosition: -51
@@ -56,7 +56,7 @@ OS2UnicodeRanges: 00000001.10000000.00000000.00000000
 Lookup: 258 0 0 "'kern' Horizontal Kerning in Latin lookup 0"  {"'kern' Horizontal Kerning in Latin lookup 0 subtable"  } ['kern' ('latn' <'dflt' > ) ]
 MarkAttachClasses: 1
 DEI: 91125
-LangName: 1033 
+LangName: 1033
 Encoding: UnicodeBmp
 UnicodeInterp: none
 NameList: Adobe Glyph List
@@ -788,7 +788,7 @@ SplineSet
  254 251 l 1
 EndSplineSet
 Validated: 1
-Kerns2: 21 -130 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  18 -150 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -110 "'kern' Horizontal Kerning in Latin lookup 0 subtable" 
+Kerns2: 21 -130 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  18 -150 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -110 "'kern' Horizontal Kerning in Latin lookup 0 subtable"
 EndChar
 
 StartChar: m
@@ -853,7 +853,7 @@ SplineSet
  -12 133 -14 134 -14 138 c 0
 EndSplineSet
 Validated: 1
-Kerns2: 18 -54 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -50 "'kern' Horizontal Kerning in Latin lookup 0 subtable" 
+Kerns2: 18 -54 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -50 "'kern' Horizontal Kerning in Latin lookup 0 subtable"
 EndChar
 
 StartChar: p
@@ -913,7 +913,7 @@ SplineSet
  105 278 120 283 134 283 c 0
 EndSplineSet
 Validated: 1
-Kerns2: 19 30 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  18 -30 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -50 "'kern' Horizontal Kerning in Latin lookup 0 subtable" 
+Kerns2: 19 30 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  18 -30 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -50 "'kern' Horizontal Kerning in Latin lookup 0 subtable"
 EndChar
 
 StartChar: r
@@ -955,7 +955,7 @@ SplineSet
  263 210 253 205 243 205 c 0x60
 EndSplineSet
 Validated: 1
-Kerns2: 21 0 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  18 -10 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -50 "'kern' Horizontal Kerning in Latin lookup 0 subtable" 
+Kerns2: 21 0 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  18 -10 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -50 "'kern' Horizontal Kerning in Latin lookup 0 subtable"
 EndChar
 
 StartChar: s
@@ -1007,7 +1007,7 @@ SplineSet
  106 242 101 229 101 214 c 0xf4
 EndSplineSet
 Validated: 1
-Kerns2: 19 40 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -30 "'kern' Horizontal Kerning in Latin lookup 0 subtable" 
+Kerns2: 19 40 "'kern' Horizontal Kerning in Latin lookup 0 subtable"  16 -30 "'kern' Horizontal Kerning in Latin lookup 0 subtable"
 EndChar
 
 StartChar: z
@@ -1054,7 +1054,7 @@ SplineSet
  91 5 55 10 34 10 c 0x30
 EndSplineSet
 Validated: 1
-Kerns2: 18 -30 "'kern' Horizontal Kerning in Latin lookup 0 subtable" 
+Kerns2: 18 -30 "'kern' Horizontal Kerning in Latin lookup 0 subtable"
 EndChar
 
 StartChar: uniE043
index d0d9aa2..a70c7f2 100644 (file)
@@ -245,12 +245,12 @@ Chord::Chord(const Chord& c, bool link)
             }
       if (c._tremolo) {
             Tremolo* t = new Tremolo(*(c._tremolo));
-            add(t);
             if (link) {
                   score()->undo(new Link(const_cast<Tremolo*>(c._tremolo), t));
                   if (c._tremolo->twoNotes())
                         t->setChords(0, 0);
                   }
+            add(t);
             }
 
       for (Element* e : c.el()) {
index 4b58719..fb77dbc 100644 (file)
@@ -1557,6 +1557,7 @@ void Score::changeAccidental(Note* note, AccidentalType accidental)
                   }
             changeAccidental2(ln, pitch, tpc);
             }
+      setPlayNote(true);
       }
 
 //---------------------------------------------------------
index 66a6859..0533244 100644 (file)
@@ -1813,6 +1813,11 @@ void Score::cmdFlip()
             selectNoteSlurMessage();
             return;
             }
+
+      std::set<const Element*> alreadyFlippedElements;
+      auto isNotFlippedElement = [&alreadyFlippedElements](const Element* element) {
+          return alreadyFlippedElements.insert(element).second;
+            };
       foreach (Element* e, el) {
             if (e->type() == Element::Type::NOTE || e->type() == Element::Type::STEM || e->type() == Element::Type::HOOK) {
                   Chord* chord;
@@ -1828,20 +1833,26 @@ void Score::cmdFlip()
                         else
                               continue;
                   else {
-                        MScore::Direction dir = chord->up() ? MScore::Direction::DOWN : MScore::Direction::UP;
-                        undoChangeProperty(chord, P_ID::STEM_DIRECTION, int(dir));
+                      if (isNotFlippedElement(chord)) {
+                            MScore::Direction dir = chord->up() ? MScore::Direction::DOWN : MScore::Direction::UP;
+                            undoChangeProperty(chord, P_ID::STEM_DIRECTION, int(dir));
+                            }
                         }
                   }
 
             if (e->type() == Element::Type::BEAM) {
-                  Beam* beam = static_cast<Beam*>(e);
-                  MScore::Direction dir = beam->up() ? MScore::Direction::DOWN : MScore::Direction::UP;
-                  undoChangeProperty(beam, P_ID::STEM_DIRECTION, int(dir));
+                  if (isNotFlippedElement(e)) {
+                        Beam* beam = static_cast<Beam*>(e);
+                        MScore::Direction dir = beam->up() ? MScore::Direction::DOWN : MScore::Direction::UP;
+                        undoChangeProperty(beam, P_ID::STEM_DIRECTION, int(dir));
+                        }
                   }
             else if (e->type() == Element::Type::SLUR_SEGMENT) {
-                  SlurTie* slur = static_cast<SlurSegment*>(e)->slurTie();
-                  MScore::Direction dir = slur->up() ? MScore::Direction::DOWN : MScore::Direction::UP;
-                  undoChangeProperty(slur, P_ID::SLUR_DIRECTION, int(dir));
+                  if (isNotFlippedElement(e)) {
+                        SlurTie* slur = static_cast<SlurSegment*>(e)->slurTie();
+                        MScore::Direction dir = slur->up() ? MScore::Direction::DOWN : MScore::Direction::UP;
+                        undoChangeProperty(slur, P_ID::SLUR_DIRECTION, int(dir));
+                        }
                   }
             else if (e->type() == Element::Type::HAIRPIN_SEGMENT) {
                   Hairpin* h = static_cast<HairpinSegment*>(e)->hairpin();
@@ -1849,37 +1860,40 @@ void Score::cmdFlip()
                   undoChangeProperty(h, P_ID::HAIRPIN_TYPE, int(st));
                   }
             else if (e->type() == Element::Type::ARTICULATION) {
-                  Articulation* a = static_cast<Articulation*>(e);
-                  if (a->articulationType() == ArticulationType::Staccato
-                     || a->articulationType() == ArticulationType::Tenuto
-                     || a->articulationType() == ArticulationType::Sforzatoaccent
-                     || a->articulationType() == ArticulationType::FadeIn
-                     || a->articulationType() == ArticulationType::FadeOut
-                     || a->articulationType() == ArticulationType::VolumeSwell
-                     || a->articulationType() == ArticulationType::WiggleSawtooth
-                     || a->articulationType() == ArticulationType::WiggleSawtoothWide
-                     || a->articulationType() == ArticulationType::WiggleVibratoLargeFaster
-                     || a->articulationType() == ArticulationType::WiggleVibratoLargeSlowest) {
-                        ArticulationAnchor aa = a->anchor();
-                        if (aa == ArticulationAnchor::TOP_CHORD)
-                              aa = ArticulationAnchor::BOTTOM_CHORD;
-                        else if (aa == ArticulationAnchor::BOTTOM_CHORD)
-                              aa = ArticulationAnchor::TOP_CHORD;
-                        else if (aa == ArticulationAnchor::CHORD)
-                              aa = a->up() ? ArticulationAnchor::BOTTOM_CHORD : ArticulationAnchor::TOP_CHORD;
-                        if (aa != a->anchor())
-                              undoChangeProperty(a, P_ID::ARTICULATION_ANCHOR, int(aa));
-                        }
-                  else {
-                        MScore::Direction d = a->up() ? MScore::Direction::DOWN : MScore::Direction::UP;
-                        undoChangeProperty(a, P_ID::DIRECTION, int(d));
+                  if (isNotFlippedElement(e)) {
+                        Articulation* a = static_cast<Articulation*>(e);
+                        if (a->articulationType() == ArticulationType::Staccato
+                           || a->articulationType() == ArticulationType::Tenuto
+                           || a->articulationType() == ArticulationType::Sforzatoaccent
+                           || a->articulationType() == ArticulationType::FadeIn
+                           || a->articulationType() == ArticulationType::FadeOut
+                           || a->articulationType() == ArticulationType::VolumeSwell
+                           || a->articulationType() == ArticulationType::WiggleSawtooth
+                           || a->articulationType() == ArticulationType::WiggleSawtoothWide
+                           || a->articulationType() == ArticulationType::WiggleVibratoLargeFaster
+                           || a->articulationType() == ArticulationType::WiggleVibratoLargeSlowest) {
+                              ArticulationAnchor aa = a->anchor();
+                              if (aa == ArticulationAnchor::TOP_CHORD)
+                                    aa = ArticulationAnchor::BOTTOM_CHORD;
+                              else if (aa == ArticulationAnchor::BOTTOM_CHORD)
+                                    aa = ArticulationAnchor::TOP_CHORD;
+                              else if (aa == ArticulationAnchor::CHORD)
+                                    aa = a->up() ? ArticulationAnchor::BOTTOM_CHORD : ArticulationAnchor::TOP_CHORD;
+                              if (aa != a->anchor())
+                                    undoChangeProperty(a, P_ID::ARTICULATION_ANCHOR, int(aa));
+                              }
+                        else {
+                              MScore::Direction d = a->up() ? MScore::Direction::DOWN : MScore::Direction::UP;
+                              undoChangeProperty(a, P_ID::DIRECTION, int(d));
+                              }
                         }
-                  //return;   // no layoutAll
                   }
             else if (e->type() == Element::Type::TUPLET) {
-                  Tuplet* tuplet = static_cast<Tuplet*>(e);
-                  MScore::Direction d = tuplet->isUp() ? MScore::Direction::DOWN : MScore::Direction::UP;
-                  undoChangeProperty(tuplet, P_ID::DIRECTION, int(d));
+                  if (isNotFlippedElement(e)) {
+                        Tuplet* tuplet = static_cast<Tuplet*>(e);
+                        MScore::Direction d = tuplet->isUp() ? MScore::Direction::DOWN : MScore::Direction::UP;
+                        undoChangeProperty(tuplet, P_ID::DIRECTION, int(d));
+                        }
                   }
             else if (e->type() == Element::Type::NOTEDOT) {
                   Note* note = static_cast<Note*>(e->parent());
index 369c43d..4e65517 100644 (file)
@@ -727,7 +727,7 @@ void Note::draw(QPainter* painter) const
                   else
                         painter->fillRect(bb, Qt::white);
 
-                  if (fretConflict() && !score()->printing()) {          //on fret conflict, draw on red background
+                  if (fretConflict() && !score()->printing() && score()->showUnprintable()) {          //on fret conflict, draw on red background
                         painter->save();
                         painter->setPen(Qt::red);
                         painter->setBrush(QBrush(QColor(Qt::red)));
index 0f061f7..5dd4a5a 100644 (file)
@@ -440,8 +440,10 @@ void RepeatList::unwindSection(Measure* sectionStartMeasure, Measure* sectionEnd
                                           for (Measure* m = _score->firstMeasure(); m; m = m->nextMeasure())
                                                 m->setPlaybackCount(0);
                                           }
-                                    else { // set each measure to have it play it's final time, but only from our jumptarget on until the current measure
-                                          for (Measure* m = jumpToMeasure; (m && (m != currentMeasure->nextMeasure())); m = m->nextMeasure()) {
+                                    else { // set each measure to have it play its final time
+                                          // but only from our jumptarget on until the playUntil/current measure (whichever comes first)
+                                          Measure* const rewindUntil = (playUntilMeasure->no() < currentMeasure->no()) ? playUntilMeasure->nextMeasure() : currentMeasure->nextMeasure();
+                                          for (Measure* m = jumpToMeasure; (m && (m != rewindUntil)); m = m->nextMeasure()) {
                                                 if (m->playbackCount() != 0)
                                                       m->setPlaybackCount(m->playbackCount() - 1);
                                                 }
index 00b9e3a..3f0817b 100644 (file)
@@ -1144,18 +1144,24 @@ bool Score::getPosition(Position* pos, const QPointF& p, int voice) const
       qreal y           = p.y() - system->pagePos().y();
       for (; pos->staffIdx < nstaves(); ++pos->staffIdx) {
             Staff* st = staff(pos->staffIdx);
-            if (st->invisible() || !st->part()->show())
+            if (!st->part()->show())
                   continue;
             qreal sy2;
             SysStaff* ss = system->staff(pos->staffIdx);
+            if (!ss->show())
+                  continue;
             SysStaff* nstaff = 0;
 
             // find next visible staff
             for (int i = pos->staffIdx + 1; i < nstaves(); ++i) {
                   Staff* st = staff(i);
-                  if (st->invisible() || !st->part()->show())
+                  if (!st->part()->show())
                         continue;
                   nstaff = system->staff(i);
+                  if (!nstaff->show()) {
+                        nstaff = 0;
+                        continue;
+                        }
                   break;
                   }
 
index 70beb0e..57f2b11 100644 (file)
@@ -208,8 +208,10 @@ endif (OMR)
 
 if (APPLE)
       file(GLOB_RECURSE INCS "*.h")
+      set(COCOABRIDGE "macos/cocoabridge.mm")
 else (APPLE)
       set(INCS "")
+      set(COCOABRIDGE "")
 endif (APPLE)
 
 add_executable ( ${ExecutableName}
@@ -221,9 +223,11 @@ add_executable ( ${ExecutableName}
       ${INCS}
 
       recordbutton.h greendotbutton prefsdialog.h
+      stringutils.h stringutils.cpp
       scoreview.cpp editinstrument.cpp editstyle.cpp
       icons.cpp importbww.cpp
-      importmxml.cpp importmxmllogger.cpp importmxmlpass1.cpp importmxmlpass2.cpp
+      importmxmllogger.cpp importmxmlnoteduration.cpp importmxmlnotepitch.cpp
+      importmxml.cpp importmxmlpass1.cpp importmxmlpass2.cpp
       instrdialog.cpp instrwidget.cpp
       debugger/debugger.cpp menus.cpp
       musescore.cpp navigator.cpp pagesettings.cpp palette.cpp
@@ -287,6 +291,7 @@ add_executable ( ${ExecutableName}
       help.cpp help.h
       toolbuttonmenu.cpp
 
+      ${COCOABRIDGE}
       ${OMR_FILES}
       ${AUDIO}
       ${SCRIPT_FILES}
@@ -421,6 +426,7 @@ if (MINGW)
             ${CROSS}/lib/libvorbis.dll
             ${CROSS}/lib/libvorbisfile.dll
             ${CROSS}/lib/portaudio.dll
+            ${CROSS}/lib/lame_enc.dll
             ${CROSS}/opt/bin/libeay32.dll
             ${CROSS}/opt/bin/ssleay32.dll
             ${CROSSQT}/bin/icudt53.dll
index 46f6d9f..ed165a8 100644 (file)
@@ -161,9 +161,12 @@ bool Album::createScore(const QString& fn, bool addPageBreak, bool addSectionBre
 
       Score* score = firstScore->clone();
 
+      LayoutMode layoutMode = score->layoutMode();
+      score->switchToPageMode();
+
       int excerptCount = firstScore->excerpts().count();
       bool joinExcerpt = true;
-       for (AlbumItem* item : _scores) {
+      for (AlbumItem* item : _scores) {
             if (item->score == 0 || item->score == firstScore)
                   continue;
             if (item->score->excerpts().count() != excerptCount) {
@@ -183,6 +186,7 @@ bool Album::createScore(const QString& fn, bool addPageBreak, bool addSectionBre
                   continue;
 
             // try to append root score
+            item->score->ScoreElement::undoChangeProperty(P_ID::LAYOUT_MODE, int(LayoutMode::PAGE));
             item->score->doLayout();
             if (!score->appendScore(item->score, addPageBreak, addSectionBreak)) {
                   qDebug("Cannot append root score of album item \"%s\".", qPrintable(item->name));
@@ -212,6 +216,8 @@ bool Album::createScore(const QString& fn, bool addPageBreak, bool addSectionBre
                   }
             }
 
+      if (layoutMode != score->layoutMode())
+            score->endCmd(true);       // rollback
       score->fileInfo()->setFile(fn);
       qDebug("Album::createScore: save file");
       try {
index 3ebd234..e8c2723 100644 (file)
@@ -297,8 +297,6 @@ class ExportMusicXml {
       void work(const MeasureBase* measure);
       void calcDivMoveToTick(int t);
       void calcDivisions();
-      double getTenthsFromInches(double);
-      double getTenthsFromDots(double);
       void keysigTimesig(const Measure* m, const Part* p);
       void chordAttributes(Chord* chord, Notations& notations, Technical& technical,
                            TrillHash& trillStart, TrillHash& trillStop);
@@ -327,7 +325,9 @@ public:
       void symbol(Symbol const* const sym, int staff);
       void tempoText(TempoText const* const text, int staff);
       void harmony(Harmony const* const, FretDiagram const* const fd, int offset = 0);
-      Score* score() { return _score; }
+      Score* score() const { return _score; };
+      double getTenthsFromInches(double) const;
+      double getTenthsFromDots(double) const;
       };
 
 //---------------------------------------------------------
@@ -1947,115 +1947,154 @@ static void fermatas(const QList<Articulation*>& cra, Xml& xml, Notations& notat
       }
 
 //---------------------------------------------------------
-//   chordAttributes
+//   symIdToArtic
 //---------------------------------------------------------
 
-void ExportMusicXml::chordAttributes(Chord* chord, Notations& notations, Technical& technical,
-                                     TrillHash& trillStart, TrillHash& trillStop)
+static QString symIdToArtic(const ArticulationType sid)
       {
-      const QList<Articulation*>& na = chord->articulations();
-      // first output the fermatas
-      fermatas(na, xml, notations);
+      switch (sid) {
+            case ArticulationType::Sforzatoaccent:
+                  return "accent";
+                  break;
 
-      // then the attributes whose elements are children of <articulations>
-      Articulations articulations;
-      for (const Articulation* a : na) {
-            switch (a->articulationType()) {
-                  case ArticulationType::Fermata:
-                  case ArticulationType::Shortfermata:
-                  case ArticulationType::Longfermata:
-                  case ArticulationType::Verylongfermata:
-                        // ignore, already handled
-                        break;
-                  case ArticulationType::Sforzatoaccent:
-                        {
-                        notations.tag(xml);
-                        articulations.tag(xml);
-                        xml.tagE("accent");
-                        }
-                        break;
-                  case ArticulationType::Staccato:
-                        {
-                        notations.tag(xml);
-                        articulations.tag(xml);
-                        xml.tagE("staccato");
-                        }
-                        break;
-                  case ArticulationType::Staccatissimo:
-                        {
-                        notations.tag(xml);
-                        articulations.tag(xml);
-                        xml.tagE("staccatissimo");
-                        }
-                        break;
-                  case ArticulationType::Tenuto:
-                        {
-                        notations.tag(xml);
-                        articulations.tag(xml);
-                        xml.tagE("tenuto");
-                        }
-                        break;
-                  case ArticulationType::Marcato:
-                        {
-                        notations.tag(xml);
-                        articulations.tag(xml);
-                        if (a->up())
-                              xml.tagE("strong-accent type=\"up\"");
-                        else
-                              xml.tagE("strong-accent type=\"down\"");
-                        }
-                        break;
-                  case ArticulationType::Portato:
-                        {
-                        notations.tag(xml);
-                        articulations.tag(xml);
-                        xml.tagE("detached-legato");
-                        }
-                        break;
-                  case ArticulationType::Reverseturn:
-                  case ArticulationType::Turn:
-                  case ArticulationType::Trill:
-                  case ArticulationType::Prall:
-                  case ArticulationType::Mordent:
-                  case ArticulationType::PrallPrall:
-                  case ArticulationType::PrallMordent:
-                  case ArticulationType::UpPrall:
-                  case ArticulationType::DownPrall:
-                  case ArticulationType::UpMordent:
-                  case ArticulationType::DownMordent:
-                  case ArticulationType::PrallDown:
-                  case ArticulationType::PrallUp:
-                  case ArticulationType::LinePrall:
-                        // ignore, handled with ornaments
-                        break;
-                  case ArticulationType::Plusstop:
-                  case ArticulationType::Upbow:
-                  case ArticulationType::Downbow:
-                  case ArticulationType::Snappizzicato:
-                  case ArticulationType::ThumbPosition:
-                        // ignore, handled with technical
-                        break;
+            case ArticulationType::Staccato:
+                  return "staccato";
+                  break;
 
-                  default:
-                        qDebug("unknown chord attribute %s", qPrintable(a->subtypeUserName()));
-                        break;
-                  }
+            case ArticulationType::Staccatissimo:
+                  return "staccatissimo";
+                  break;
+
+            case ArticulationType::Tenuto:
+                  return "tenuto";
+                  break;
+
+            case ArticulationType::Marcato:
+                  return "strong-accent";
+                  break;
+
+            case ArticulationType::Portato:
+                  return "detached-legato";
+                  break;
+
+            default:
+                  ;             // nothing
+                  break;
             }
 
-      if (Breath* b = hasBreathMark(chord)) {
-            notations.tag(xml);
-            articulations.tag(xml);
-            int st = b->breathType();
-            if (st == 0 || st == 1)
-                  xml.tagE("breath-mark");
-            else
-                  xml.tagE("caesura");
+      return "";
+      }
+
+//---------------------------------------------------------
+//   symIdToOrnam
+//---------------------------------------------------------
+
+static QString symIdToOrnam(const ArticulationType sid)
+      {
+      switch (sid) {
+            case ArticulationType::Reverseturn:
+                  return "inverted-turn";
+                  break;
+            case ArticulationType::Turn:
+                  return "turn";
+                  break;
+            case ArticulationType::Trill:
+                  return "trill-mark";
+                  break;
+            case ArticulationType::Prall:
+                  return "inverted-mordent";
+                  break;
+            case ArticulationType::Mordent:
+                  return "mordent";
+                  break;
+            case ArticulationType::PrallPrall:
+                  return "inverted-mordent long=\"yes\"";
+                  break;
+            case ArticulationType::PrallMordent:
+                  return "mordent long=\"yes\"";
+                  break;
+            case ArticulationType::UpPrall:
+                  return "inverted-mordent long=\"yes\" approach=\"below\"";
+                  break;
+            case ArticulationType::DownPrall:
+                  return "inverted-mordent long=\"yes\" approach=\"above\"";
+                  break;
+            case ArticulationType::UpMordent:
+                  return "mordent long=\"yes\" approach=\"below\"";
+                  break;
+            case ArticulationType::DownMordent:
+                  return "mordent long=\"yes\" approach=\"above\"";
+                  break;
+            case ArticulationType::PrallDown:
+                  return "inverted-mordent long=\"yes\" departure=\"below\"";
+                  break;
+            case ArticulationType::PrallUp:
+                  return "inverted-mordent long=\"yes\" departure=\"above\"";
+                  break;
+            case ArticulationType::LinePrall:
+                  // MusicXML 3.0 does not distinguish between downprall and lineprall
+                  return "inverted-mordent long=\"yes\" approach=\"above\"";
+                  break;
+            case ArticulationType::Schleifer:
+                  return "schleifer";
+                  break;
+
+            default:
+                  ;       // nothing
+                  break;
             }
 
+      return "";
+      }
+
+//---------------------------------------------------------
+//   symIdToTechn
+//---------------------------------------------------------
+
+static QString symIdToTechn(const ArticulationType sid)
+      {
+      switch (sid) {
+            case ArticulationType::Plusstop:
+                  return "stopped";
+                  break;
+            /*
+      case SymId::stringsHarmonic:
+            return "x"; // will be overruled but must be non-empty
+            break;
+             */
+            case ArticulationType::Upbow:
+                  return "up-bow";
+                  break;
+            case ArticulationType::Downbow:
+                  return "down-bow";
+                  break;
+            case ArticulationType::Snappizzicato:
+                  return "snap-pizzicato";
+                  break;
+            case ArticulationType::Ouvert:
+                  return "open-string";
+                  break;
+            case ArticulationType::ThumbPosition:
+                  return "thumb-position";
+                  break;
+            default:
+                  ;       // nothing
+                  break;
+            }
+
+      return "";
+      }
+
+//---------------------------------------------------------
+//   writeChordLines
+//---------------------------------------------------------
+
+static void writeChordLines(const Chord* const chord, Xml& xml, Notations& notations, Articulations& articulations)
+      {
       for (Element* e : chord->el()) {
             qDebug("chordAttributes: el %p type %d (%s)", e, int(e->type()), e->name());
             if (e->type() == Element::Type::CHORDLINE) {
-                  ChordLine const* const cl = static_cast<ChordLine const* const>(e);
+                  ChordLine const* const cl = static_cast<ChordLine*>(e);
                   QString subtype;
                   switch (cl->chordLineType()) {
                         case ChordLineType::FALL:
@@ -2080,196 +2119,85 @@ void ExportMusicXml::chordAttributes(Chord* chord, Notations& notations, Technic
                         }
                   }
             }
+      }
+
+//---------------------------------------------------------
+//   chordAttributes
+//---------------------------------------------------------
+
+void ExportMusicXml::chordAttributes(Chord* chord, Notations& notations, Technical& technical,
+                                     TrillHash& trillStart, TrillHash& trillStop)
+      {
+      const QList<Articulation*>& na = chord->articulations();
+      // first output the fermatas
+      fermatas(na, xml, notations);
+
+      //const QList<Articulation*> na = chord->articulations();
+      // then the attributes whose elements are children of <articulations>
+      Articulations articulations;
+      for (const Articulation* a : na) {
+            auto mxmlArtic = symIdToArtic(a->articulationType());
+
+            if (mxmlArtic != "") {
+                  if (a->articulationType() == ArticulationType::Marcato) {
+                        if (a->up())
+                              mxmlArtic += " type=\"up\"";
+                        else
+                              mxmlArtic += " type=\"down\"";
+                        }
+
+                  notations.tag(xml);
+                  articulations.tag(xml);
+                  xml.tagE(mxmlArtic);
+                  }
+            }
+
+      if (Breath* b = hasBreathMark(chord)) {
+            notations.tag(xml);
+            articulations.tag(xml);
+            int st = b->breathType();
+            if (st == 0 || st == 1)
+                  xml.tagE("breath-mark");
+            else
+                  xml.tagE("caesura");
+            }
+
+      writeChordLines(chord, xml, notations, articulations);
 
       articulations.etag(xml);
 
       // then the attributes whose elements are children of <ornaments>
       Ornaments ornaments;
       for (const Articulation* a : na) {
-            switch (a->articulationType()) {
-                  case ArticulationType::Fermata:
-                  case ArticulationType::Shortfermata:
-                  case ArticulationType::Longfermata:
-                  case ArticulationType::Verylongfermata:
-                  case ArticulationType::Sforzatoaccent:
-                  case ArticulationType::Staccato:
-                  case ArticulationType::Staccatissimo:
-                  case ArticulationType::Tenuto:
-                  case ArticulationType::Marcato:
-                  case ArticulationType::Portato:
-                        // ignore, already handled
-                        break;
-                  case ArticulationType::Reverseturn:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("inverted-turn");
-                        }
-                        break;
-                  case ArticulationType::Turn:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("turn");
-                        }
-                        break;
-                  case ArticulationType::Trill:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("trill-mark");
-                        }
-                        break;
-                  case ArticulationType::Prall:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("inverted-mordent");
-                        }
-                        break;
-                  case ArticulationType::Mordent:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("mordent");
-                        }
-                        break;
-                  case ArticulationType::PrallPrall:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("inverted-mordent long=\"yes\"");
-                        }
-                        break;
-                  case ArticulationType::PrallMordent:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("mordent long=\"yes\"");
-                        }
-                        break;
-                  case ArticulationType::UpPrall:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("inverted-mordent long=\"yes\" approach=\"below\"");
-                        }
-                        break;
-                  case ArticulationType::DownPrall:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("inverted-mordent long=\"yes\" approach=\"above\"");
-                        }
-                        break;
-                  case ArticulationType::UpMordent:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("mordent long=\"yes\" approach=\"below\"");
-                        }
-                        break;
-                  case ArticulationType::DownMordent:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("mordent long=\"yes\" approach=\"above\"");
-                        }
-                        break;
-                  case ArticulationType::PrallDown:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("inverted-mordent long=\"yes\" departure=\"below\"");
-                        }
-                        break;
-                  case ArticulationType::PrallUp:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("inverted-mordent long=\"yes\" departure=\"above\"");
-                        }
-                        break;
-                  case ArticulationType::LinePrall:
-                        {
-                        // MusicXML 3.0 does not distinguish between downprall and lineprall
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("inverted-mordent long=\"yes\" approach=\"above\"");
-                        }
-                        break;
-                  case ArticulationType::Schleifer:
-                        {
-                        notations.tag(xml);
-                        ornaments.tag(xml);
-                        xml.tagE("schleifer");
-                        }
-                        break;
-                  case ArticulationType::Plusstop:
-                  case ArticulationType::Upbow:
-                  case ArticulationType::Downbow:
-                  case ArticulationType::Snappizzicato:
-                  case ArticulationType::ThumbPosition:
-                        // ignore, handled with technical
-                        break;
-                  default:
-                        qDebug("unknown chord attribute %s", qPrintable(a->subtypeUserName()));
-                        break;
+            auto mxmlOrnam = symIdToOrnam(a->articulationType());
+            if (mxmlOrnam != "") {
+                  notations.tag(xml);
+                  ornaments.tag(xml);
+                  xml.tagE(mxmlOrnam);
                   }
             }
+
       tremoloSingleStartStop(chord, notations, ornaments, xml);
       wavyLineStartStop(chord, notations, ornaments, trillStart, trillStop);
       ornaments.etag(xml);
 
       // and finally the attributes whose elements are children of <technical>
       for (const Articulation* a : na) {
-            switch (a->articulationType()) {
-                  case ArticulationType::Plusstop:
-                        {
-                        notations.tag(xml);
-                        technical.tag(xml);
-                        xml.tagE("stopped");
-                        }
-                        break;
-                  case ArticulationType::Upbow:
-                        {
-                        notations.tag(xml);
-                        technical.tag(xml);
-                        xml.tagE("up-bow");
-                        }
-                        break;
-                  case ArticulationType::Downbow:
-                        {
-                        notations.tag(xml);
-                        technical.tag(xml);
-                        xml.tagE("down-bow");
-                        }
-                        break;
-                  case ArticulationType::Snappizzicato:
-                        {
-                        notations.tag(xml);
-                        technical.tag(xml);
-                        xml.tagE("snap-pizzicato");
-                        }
-                        break;
-                  case ArticulationType::Ouvert:
-                        {
-                        notations.tag(xml);
-                        technical.tag(xml);
-                        xml.tagE("open-string");
-                        }
-                        break;
-                  case ArticulationType::ThumbPosition:
-                        {
-                        notations.tag(xml);
-                        technical.tag(xml);
-                        xml.tagE("thumb-position");
-                        }
-                        break;
-                  default:
-                        // others silently ignored
-                        // qDebug("unknown chord attribute %s", qPrintable(a->subtypeUserName()));
-                        break;
+            auto mxmlTechn = symIdToTechn(a->articulationType());
+            if (mxmlTechn != "") {
+                  notations.tag(xml);
+                  technical.tag(xml);
+                  xml.tagE(mxmlTechn);
+                  }
+            }
+
+      // check if all articulations were handled
+      for (const Articulation* a : na) {
+            auto sid = a->articulationType();
+            if (symIdToArtic(sid) == ""
+                && symIdToOrnam(sid) == ""
+                && symIdToTechn(sid) == "") {
+                  qDebug("unknown chord attribute %s", qPrintable(a->userName()));
                   }
             }
       }
@@ -2320,29 +2248,6 @@ static void arpeggiate(Arpeggio* arp, bool front, bool back, Xml& xml, Notations
             xml.tagE(tagName);
       }
 
-// find the next chord in the same track
-/* NO LONGER NEEDED
-static Chord* nextChord(Chord* ch)
-      {
-      Segment* s = ch->segment();
-      s = s->next1();
-      while (s) {
-            if (s->segmentType() == Segment::Type::ChordRest && s->element(ch->track()))
-                  break;
-            s = s->next1();
-            }
-      if (s == 0) {
-            // qDebug("no segment for second note of glissando found");
-            return 0;
-            }
-      Element* el = s->element(ch->track());
-      if (el == 0 || el->type() != Element::Type::CHORD) {
-            // qDebug("no second note for glissando found, track %d", track());
-            return 0;
-            }
-      return static_cast<Chord*>(el);
-      }
-*/
 //---------------------------------------------------------
 //   determineTupletNormalTicks
 //---------------------------------------------------------
@@ -2378,6 +2283,13 @@ static int determineTupletNormalTicks(ChordRest const* const chord)
 //   writeBeam
 //---------------------------------------------------------
 
+//  beaming
+//    <beam number="1">start</beam>
+//    <beam number="1">end</beam>
+//    <beam number="1">continue</beam>
+//    <beam number="1">backward hook</beam>
+//    <beam number="1">forward hook</beam>
+
 static void writeBeam(Xml& xml, ChordRest* cr, Beam* b)
       {
       const QList<ChordRest*>& elements = b->elements();
@@ -2430,6 +2342,303 @@ static QString instrId(int partNr, int instrNr)
       }
 
 //---------------------------------------------------------
+//   writeNotehead
+//---------------------------------------------------------
+
+static void writeNotehead(Xml& xml, const Note* const note)
+      {
+      QString noteheadTagname = QString("notehead");
+      noteheadTagname += color2xml(note);
+      bool leftParenthesis, rightParenthesis = false;
+      for (Element* elem : note->el()) {
+            if (elem->type() == Element::Type::SYMBOL) {
+                  Symbol* s = static_cast<Symbol*>(elem);
+                  if (s->sym() == SymId::noteheadParenthesisLeft)
+                        leftParenthesis = true;
+                  else if (s->sym() == SymId::noteheadParenthesisRight)
+                        rightParenthesis = true;
+                  }
+            }
+      if (rightParenthesis && leftParenthesis)
+            noteheadTagname += " parentheses=\"yes\"";
+      if (note->headType() == NoteHead::Type::HEAD_QUARTER)
+            noteheadTagname += " filled=\"yes\"";
+      else if ((note->headType() == NoteHead::Type::HEAD_HALF) || (note->headType() == NoteHead::Type::HEAD_WHOLE))
+            noteheadTagname += " filled=\"no\"";
+      if (note->headGroup() == NoteHead::Group::HEAD_SLASH)
+            xml.tag(noteheadTagname, "slash");
+      else if (note->headGroup() == NoteHead::Group::HEAD_TRIANGLE)
+            xml.tag(noteheadTagname, "triangle");
+      else if (note->headGroup() == NoteHead::Group::HEAD_DIAMOND)
+            xml.tag(noteheadTagname, "diamond");
+      /*
+      else if (note->headGroup() == NoteHead::Group::HEAD_PLUS)
+            xml.tag(noteheadTagname, "cross");
+       */
+      else if (note->headGroup() == NoteHead::Group::HEAD_CROSS)
+            xml.tag(noteheadTagname, "x");
+      else if (note->headGroup() == NoteHead::Group::HEAD_XCIRCLE)
+            xml.tag(noteheadTagname, "circle-x");
+      /*
+      else if (note->headGroup() == NoteHead::Group::HEAD_TRIANGLE_DOWN)
+            xml.tag(noteheadTagname, "inverted triangle");
+      else if (note->headGroup() == NoteHead::Group::HEAD_SLASHED1)
+            xml.tag(noteheadTagname, "slashed");
+      else if (note->headGroup() == NoteHead::Group::HEAD_SLASHED2)
+            xml.tag(noteheadTagname, "back slashed");
+       */
+      else if (note->headGroup() == NoteHead::Group::HEAD_DO)
+            xml.tag(noteheadTagname, "do");
+      else if (note->headGroup() == NoteHead::Group::HEAD_RE)
+            xml.tag(noteheadTagname, "re");
+      else if (note->headGroup() == NoteHead::Group::HEAD_MI)
+            xml.tag(noteheadTagname, "mi");
+      else if (note->headGroup() == NoteHead::Group::HEAD_FA)
+            xml.tag(noteheadTagname, "fa");
+      else if (note->headGroup() == NoteHead::Group::HEAD_LA)
+            xml.tag(noteheadTagname, "la");
+      else if (note->headGroup() == NoteHead::Group::HEAD_TI)
+            xml.tag(noteheadTagname, "ti");
+      else if (note->headGroup() == NoteHead::Group::HEAD_SOL)
+            xml.tag(noteheadTagname, "so");
+      else if (note->color() != MScore::defaultColor)
+            xml.tag(noteheadTagname, "normal");
+      else if (rightParenthesis && leftParenthesis)
+            xml.tag(noteheadTagname, "normal");
+      else if (note->headType() != NoteHead::Type::HEAD_AUTO)
+            xml.tag(noteheadTagname, "normal");
+      }
+
+//---------------------------------------------------------
+//   writeFingering
+//---------------------------------------------------------
+
+static void writeFingering(Xml& xml, Notations& notations, Technical& technical, const Note* const note)
+      {
+      for (const Element* e : note->el()) {
+            if (e->type() == Element::Type::FINGERING) {
+                  Text* f = (Text*)e;
+                  notations.tag(xml);
+                  technical.tag(xml);
+                  QString t = MScoreTextToMXML::toPlainText(f->xmlText());
+                  if (f->textStyleType() == TextStyleType::RH_GUITAR_FINGERING)
+                        xml.tag("pluck", t);
+                  else if (f->textStyleType() == TextStyleType::LH_GUITAR_FINGERING)
+                        xml.tag("fingering", t);
+                  else if (f->textStyleType() == TextStyleType::FINGERING) {
+                        // for generic fingering, try to detect plucking
+                        // (backwards compatibility with MuseScore 1.x)
+                        // p, i, m, a, c represent the plucking finger
+                        if (t == "p" || t == "i" || t == "m" || t == "a" || t == "c")
+                              xml.tag("pluck", t);
+                        else
+                              xml.tag("fingering", t);
+                        }
+                  else if (f->textStyleType() == TextStyleType::STRING_NUMBER) {
+                        bool ok;
+                        int i = t.toInt(&ok);
+                        if (ok) {
+                              if (i == 0)
+                                    xml.tagE("open-string");
+                              else if (i > 0)
+                                    xml.tag("string", t);
+                              }
+                        if (!ok || i < 0)
+                              qDebug("invalid string number '%s'", qPrintable(t));
+                        }
+                  else
+                        qDebug("unknown fingering style");
+                  }
+            else {
+                  // TODO
+                  }
+            }
+      }
+
+//---------------------------------------------------------
+//   stretchCorrActTicks
+//---------------------------------------------------------
+
+static int stretchCorrActTicks(const Note* const note)
+      {
+      // time signature stretch factor
+      const Fraction str = note->chord()->staff()->timeStretch(note->chord()->tick());
+      // chord's actual ticks corrected for stretch
+      return note->chord()->actualTicks() * str.numerator() / str.denominator();
+      }
+
+//---------------------------------------------------------
+//   tremoloCorrection
+//---------------------------------------------------------
+
+// duration correction for two note tremolo
+static int tremoloCorrection(const Note* const note)
+      {
+      int tremCorr = 1;
+      if (isTwoNoteTremolo(note->chord())) tremCorr = 2;
+      return tremCorr;
+      }
+
+//---------------------------------------------------------
+//   writeTypeAndDots
+//---------------------------------------------------------
+
+static void writeTypeAndDots(Xml& xml, const Note* const note)
+      {
+      // type
+      int dots = 0;
+      Tuplet* t = note->chord()->tuplet();
+      int actNotes = 1;
+      int nrmNotes = 1;
+      if (t) {
+            actNotes = t->ratio().numerator();
+            nrmNotes = t->ratio().denominator();
+            }
+
+      const auto strActTicks = stretchCorrActTicks(note);
+      QString s = tick2xml(strActTicks * actNotes * tremoloCorrection(note) / nrmNotes, &dots);
+      if (s.isEmpty())
+            qDebug("no note type found for ticks %d", strActTicks);
+
+      if (note->small())
+            xml.tag("type size=\"cue\"", s);
+      else
+            xml.tag("type", s);
+      for (int ni = dots; ni > 0; ni--)
+            xml.tagE("dot");
+      }
+
+//---------------------------------------------------------
+//   writeTimeModification
+//---------------------------------------------------------
+
+static void writeTimeModification(Xml& xml, const Note* const note)
+      {
+      // time modification for two note tremolo
+      // TODO: support tremolo in tuplet ?
+      if (tremoloCorrection(note) == 2) {
+            xml.stag("time-modification");
+            xml.tag("actual-notes", 2);
+            xml.tag("normal-notes", 1);
+            xml.etag();
+            }
+
+      // time modification for tuplet
+      const auto t = note->chord()->tuplet();
+      if (t) {
+            auto actNotes = t->ratio().numerator();
+            auto nrmNotes = t->ratio().denominator();
+            auto nrmTicks = determineTupletNormalTicks(note->chord());
+            // TODO: remove following duplicated code (present for both notes and rests)
+            xml.stag("time-modification");
+            xml.tag("actual-notes", actNotes);
+            xml.tag("normal-notes", nrmNotes);
+            //qDebug("nrmTicks %d", nrmTicks);
+            if (nrmTicks > 0) {
+                  int nrmDots = 0;
+                  QString nrmType = tick2xml(nrmTicks, &nrmDots);
+                  if (nrmType.isEmpty())
+                        qDebug("no note type found for ticks %d", nrmTicks);
+                  else {
+                        xml.tag("normal-type", nrmType);
+                        for (int ni = nrmDots; ni > 0; ni--)
+                              xml.tagE("normal-dot");
+                        }
+                  }
+            xml.etag();
+            }
+      }
+
+//---------------------------------------------------------
+//   writePitch
+//---------------------------------------------------------
+
+static void writePitch(Xml& xml, const Note* const note, const bool useDrumset)
+      {
+      // step / alter / octave
+      QString step;
+      int alter = 0;
+      int octave = 0;
+      const auto chord = note->chord();
+      if (chord->staff() && chord->staff()->isTabStaff()) {
+            tabpitch2xml(note->pitch(), note->tpc(), step, alter, octave);
+            }
+      else {
+            if (!useDrumset) {
+                  pitch2xml(note, step, alter, octave);
+                  }
+            else {
+                  unpitch2xml(note, step, octave);
+                  }
+            }
+      xml.stag(useDrumset ? "unpitched" : "pitch");
+      xml.tag(useDrumset  ? "display-step" : "step", step);
+      // Check for microtonal accidentals and overwrite "alter" tag
+      auto acc = note->accidental();
+      double alter2 = 0.0;
+      if (acc) {
+            switch (acc->accidentalType()) {
+                  case AccidentalType::MIRRORED_FLAT:  alter2 = -0.5; break;
+                  case AccidentalType::SHARP_SLASH:    alter2 = 0.5;  break;
+                  case AccidentalType::MIRRORED_FLAT2: alter2 = -1.5; break;
+                  case AccidentalType::SHARP_SLASH4:   alter2 = 1.5;  break;
+                  default:                                             break;
+                  }
+            }
+      if (alter && !alter2)
+            xml.tag("alter", alter);
+      if (!alter && alter2)
+            xml.tag("alter", alter2);
+      // TODO what if both alter and alter2 are present? For Example: playing with transposing instruments
+      xml.tag(useDrumset ? "display-octave" : "octave", octave);
+      xml.etag();
+      }
+
+//---------------------------------------------------------
+//   writeAccidental
+//---------------------------------------------------------
+
+static void writeAccidental(Xml& xml, const Note* const note)
+      {
+      auto acc = note->accidental();
+      if (acc) {
+            QString s = accidentalType2MxmlString(acc->accidentalType());
+            if (s != "") {
+                  if (note->accidental()->hasBracket())
+                        xml.tag("accidental parentheses=\"yes\"", s);
+                  else
+                        xml.tag("accidental", s);
+                  }
+            }
+      }
+
+//---------------------------------------------------------
+//   notePosition
+//---------------------------------------------------------
+
+static QString notePosition(const ExportMusicXml* const expMxml, const Note* const note)
+      {
+      QString res;
+
+      if (preferences.musicxmlExportLayout) {
+            const PageFormat* pf = note->score()->pageFormat();
+            const double pageHeight  = expMxml->getTenthsFromInches(pf->size().height());
+
+            const auto chord = note->chord();
+
+            double measureX = expMxml->getTenthsFromDots(chord->measure()->pagePos().x());
+            double measureY = pageHeight - expMxml->getTenthsFromDots(chord->measure()->pagePos().y());
+            double noteX = expMxml->getTenthsFromDots(note->pagePos().x());
+            double noteY = pageHeight - expMxml->getTenthsFromDots(note->pagePos().y());
+
+            res += QString(" default-x=\"%1\"").arg(QString::number(noteX - measureX,'f',2));
+            res += QString(" default-y=\"%1\"").arg(QString::number(noteY - measureY,'f',2));
+            }
+
+      return res;
+      }
+
+//---------------------------------------------------------
 //   chord
 //---------------------------------------------------------
 
@@ -2454,8 +2663,6 @@ void ExportMusicXml::chord(Chord* chord, int staff, const QList<Lyrics*>* ll, bo
        */
       QList<Note*> nl = chord->notes();
       bool grace = chord->isGrace();
-      int tremCorr = 1; // duration correction for two note tremolo
-      if (isTwoNoteTremolo(chord)) tremCorr = 2;
       if (!grace) tick += chord->actualTicks();
 #ifdef DEBUG_TICK
       qDebug("ExportMusicXml::chord() oldtick=%d", tick);
@@ -2463,25 +2670,13 @@ void ExportMusicXml::chord(Chord* chord, int staff, const QList<Lyrics*>* ll, bo
       qDebug(" newtick=%d", tick);
 #endif
 
-      const PageFormat* pf = _score->pageFormat();
-      const double pageHeight  = getTenthsFromInches(pf->size().height());
-      // const double pageWidth  = getTenthsFromInches(pf->size().width());
-
       for (Note* note : nl) {
             QString val;
 
             attr.doAttr(xml, false);
             QString noteTag = QString("note");
 
-            if (preferences.musicxmlExportLayout && pf) {
-                  double measureX = getTenthsFromDots(chord->measure()->pagePos().x());
-                  double measureY = pageHeight - getTenthsFromDots(chord->measure()->pagePos().y());
-                  double noteX = getTenthsFromDots(note->pagePos().x());
-                  double noteY = pageHeight - getTenthsFromDots(note->pagePos().y());
-
-                  noteTag += QString(" default-x=\"%1\"").arg(QString::number(noteX - measureX,'f',2));
-                  noteTag += QString(" default-y=\"%1\"").arg(QString::number(noteY - measureY,'f',2));
-                  }
+            noteTag += notePosition(this, note);
 
             if (!note->visible()) {
                   noteTag += QString(" print-object=\"no\"");
@@ -2504,51 +2699,11 @@ void ExportMusicXml::chord(Chord* chord, int staff, const QList<Lyrics*>* ll, bo
             else if (note->chord()->small()) // need this only once per chord
                   xml.tagE("cue");
 
-            // step / alter / octave
-            QString step;
-            int alter = 0;
-            int octave = 0;
-            if (chord->staff() && chord->staff()->isTabStaff()) {
-                  tabpitch2xml(note->pitch(), note->tpc(), step, alter, octave);
-                  }
-            else {
-                  if (!useDrumset) {
-                        pitch2xml(note, step, alter, octave);
-                        }
-                  else {
-                        unpitch2xml(note, step, octave);
-                        }
-                  }
-            xml.stag(useDrumset ? "unpitched" : "pitch");
-            xml.tag(useDrumset  ? "display-step" : "step", step);
-            // Check for microtonal accidentals and overwrite "alter" tag
-            Accidental* acc = note->accidental();
-            double alter2 = 0.0;
-            if (acc) {
-                  switch (acc->accidentalType()) {
-                        case AccidentalType::MIRRORED_FLAT:  alter2 = -0.5; break;
-                        case AccidentalType::SHARP_SLASH:    alter2 = 0.5;  break;
-                        case AccidentalType::MIRRORED_FLAT2: alter2 = -1.5; break;
-                        case AccidentalType::SHARP_SLASH4:   alter2 = 1.5;  break;
-                        default:                                             break;
-                        }
-                  }
-            if (alter && !alter2)
-                  xml.tag("alter", alter);
-            if (!alter && alter2)
-                  xml.tag("alter", alter2);
-            // TODO what if both alter and alter2 are present? For Example: playing with transposing instruments
-            xml.tag(useDrumset ? "display-octave" : "octave", octave);
-            xml.etag();
-
-            // time signature stretch factor
-            const Fraction str = note->chord()->staff()->timeStretch(note->chord()->tick());
-            // chord's actual ticks corrected for stretch
-            const int strActTicks = note->chord()->actualTicks() * str.numerator() / str.denominator();
+            writePitch(xml, note, useDrumset);
 
             // duration
             if (!grace)
-                  xml.tag("duration", strActTicks / div);
+                  xml.tag("duration", stretchCorrActTicks(note) / div);
 
             if (note->tieBack())
                   xml.tagE("tie type=\"stop\"");
@@ -2572,138 +2727,24 @@ void ExportMusicXml::chord(Chord* chord, int staff, const QList<Lyrics*>* ll, bo
 
             xml.tag("voice", voice);
 
-            // type
-            int dots = 0;
-            Tuplet* t = note->chord()->tuplet();
-            int actNotes = 1;
-            int nrmNotes = 1;
-            int nrmTicks = 0;
-            if (t) {
-                  actNotes = t->ratio().numerator();
-                  nrmNotes = t->ratio().denominator();
-                  nrmTicks = determineTupletNormalTicks(chord);
-                  }
-
-            QString s = tick2xml(strActTicks * actNotes * tremCorr / nrmNotes, &dots);
-            if (s.isEmpty())
-                  qDebug("no note type found for ticks %d", strActTicks);
-
-            if (note->small())
-                  xml.tag("type size=\"cue\"", s);
-            else
-                  xml.tag("type", s);
-            for (int ni = dots; ni > 0; ni--)
-                  xml.tagE("dot");
-
-            // accidental
-            if (acc) {
-                  QString s = accidentalType2MxmlString(acc->accidentalType());
-                  if (s != "") {
-                        if (note->accidental()->hasBracket())
-                              xml.tag("accidental parentheses=\"yes\"", s);
-                        else
-                              xml.tag("accidental", s);
-                        }
-                  }
-
-            // time modification for two note tremolo
-            // TODO: support tremolo in tuplet ?
-            if (tremCorr == 2) {
-                  xml.stag("time-modification");
-                  xml.tag("actual-notes", 2);
-                  xml.tag("normal-notes", 1);
-                  xml.etag();
-                  }
-
-            // time modification for tuplet
-            if (t) {
-                  // TODO: remove following duplicated code (present for both notes and rests)
-                  xml.stag("time-modification");
-                  xml.tag("actual-notes", actNotes);
-                  xml.tag("normal-notes", nrmNotes);
-                  //qDebug("nrmTicks %d", nrmTicks);
-                  if (nrmTicks > 0) {
-                        int nrmDots = 0;
-                        QString nrmType = tick2xml(nrmTicks, &nrmDots);
-                        if (nrmType.isEmpty())
-                              qDebug("no note type found for ticks %d", nrmTicks);
-                        else {
-                              xml.tag("normal-type", nrmType);
-                              for (int ni = nrmDots; ni > 0; ni--)
-                                    xml.tagE("normal-dot");
-                              }
-                        }
-                  xml.etag();
-                  }
+            writeTypeAndDots(xml, note);
+            writeAccidental(xml, note);
+            writeTimeModification(xml, note);
 
             // no stem for whole notes and beyond
             if (chord->noStem() || chord->measure()->slashStyle(chord->staffIdx())) {
                   xml.tag("stem", QString("none"));
                   }
-            else if ((note->chord()->actualTicks() * actNotes * tremCorr / nrmNotes) < (4 * MScore::division)) {
+            else if (note->chord()->stem()) {
                   xml.tag("stem", QString(note->chord()->up() ? "up" : "down"));
                   }
 
-            QString noteheadTagname = QString("notehead");
-            noteheadTagname += color2xml(note);
-            bool leftParenthesis, rightParenthesis = false;
-            for (Element* elem : note->el()) {
-                  if (elem->type() == Element::Type::SYMBOL) {
-                        Symbol* s = static_cast<Symbol*>(elem);
-                        if (s->sym() == SymId::noteheadParenthesisLeft)
-                              leftParenthesis = true;
-                        else if (s->sym() == SymId::noteheadParenthesisRight)
-                              rightParenthesis = true;
-                        }
-                  }
-            if (rightParenthesis && leftParenthesis)
-                  noteheadTagname += " parentheses=\"yes\"";
-            if (note->headType() == NoteHead::Type::HEAD_QUARTER)
-                  noteheadTagname += " filled=\"yes\"";
-            else if ((note->headType() == NoteHead::Type::HEAD_HALF) || (note->headType() == NoteHead::Type::HEAD_WHOLE))
-                  noteheadTagname += " filled=\"no\"";
-            if (note->headGroup() == NoteHead::Group::HEAD_SLASH)
-                  xml.tag(noteheadTagname, "slash");
-            else if (note->headGroup() == NoteHead::Group::HEAD_TRIANGLE)
-                  xml.tag(noteheadTagname, "triangle");
-            else if (note->headGroup() == NoteHead::Group::HEAD_DIAMOND)
-                  xml.tag(noteheadTagname, "diamond");
-            else if (note->headGroup() == NoteHead::Group::HEAD_CROSS)
-                  xml.tag(noteheadTagname, "x");
-            else if (note->headGroup() == NoteHead::Group::HEAD_XCIRCLE)
-                  xml.tag(noteheadTagname, "circle-x");
-            else if (note->headGroup() == NoteHead::Group::HEAD_DO)
-                  xml.tag(noteheadTagname, "do");
-            else if (note->headGroup() == NoteHead::Group::HEAD_RE)
-                  xml.tag(noteheadTagname, "re");
-            else if (note->headGroup() == NoteHead::Group::HEAD_MI)
-                  xml.tag(noteheadTagname, "mi");
-            else if (note->headGroup() == NoteHead::Group::HEAD_FA)
-                  xml.tag(noteheadTagname, "fa");
-            else if (note->headGroup() == NoteHead::Group::HEAD_LA)
-                  xml.tag(noteheadTagname, "la");
-            else if (note->headGroup() == NoteHead::Group::HEAD_TI)
-                  xml.tag(noteheadTagname, "ti");
-            else if (note->headGroup() == NoteHead::Group::HEAD_SOL)
-                  xml.tag(noteheadTagname, "so");
-            else if (note->color() != MScore::defaultColor)
-                  xml.tag(noteheadTagname, "normal");
-            else if (rightParenthesis && leftParenthesis)
-                  xml.tag(noteheadTagname, "normal");
-            else if (note->headType() != NoteHead::Type::HEAD_AUTO)
-                  xml.tag(noteheadTagname, "normal");
+            writeNotehead(xml, note);
 
             // LVIFIX: check move() handling
             if (staff)
                   xml.tag("staff", staff + note->chord()->staffMove());
 
-            //  beaming
-            //    <beam number="1">start</beam>
-            //    <beam number="1">end</beam>
-            //    <beam number="1">continue</beam>
-            //    <beam number="1">backward hook</beam>
-            //    <beam number="1">forward hook</beam>
-
             if (note == nl.front() && chord->beam())
                   writeBeam(xml, chord, chord->beam());
 
@@ -2731,44 +2772,7 @@ void ExportMusicXml::chord(Chord* chord, int staff, const QList<Lyrics*>* ll, bo
                   chordAttributes(chord, notations, technical, trillStart, trillStop);
                   }
 
-            for (const Element* e : note->el()) {
-                  if (e->type() == Element::Type::FINGERING) {
-                        Text* f = (Text*)e;
-                        notations.tag(xml);
-                        technical.tag(xml);
-                        QString t = MScoreTextToMXML::toPlainText(f->xmlText());
-                        if (f->textStyleType() == TextStyleType::RH_GUITAR_FINGERING)
-                              xml.tag("pluck", t);
-                        else if (f->textStyleType() == TextStyleType::LH_GUITAR_FINGERING)
-                              xml.tag("fingering", t);
-                        else if (f->textStyleType() == TextStyleType::FINGERING) {
-                              // for generic fingering, try to detect plucking
-                              // (backwards compatibility with MuseScore 1.x)
-                              // p, i, m, a, c represent the plucking finger
-                              if (t == "p" || t == "i" || t == "m" || t == "a" || t == "c")
-                                    xml.tag("pluck", t);
-                              else
-                                    xml.tag("fingering", t);
-                              }
-                        else if (f->textStyleType() == TextStyleType::STRING_NUMBER) {
-                              bool ok;
-                              int i = t.toInt(&ok);
-                              if (ok) {
-                                    if (i == 0)
-                                          xml.tagE("open-string");
-                                    else if (i > 0)
-                                          xml.tag("string", t);
-                                    }
-                              if (!ok || i < 0)
-                                    qDebug("invalid string number '%s'", qPrintable(t));
-                              }
-                        else
-                              qDebug("unknown fingering style");
-                        }
-                  else {
-                        // TODO
-                        }
-                  }
+            writeFingering(xml, notations, technical, note);
 
             // write tablature string / fret
             if (chord->staff() && chord->staff()->isTabStaff())
@@ -5371,12 +5375,12 @@ bool saveMxl(Score* score, const QString& name)
       return true;
       }
 
-double ExportMusicXml::getTenthsFromInches(double inches)
+double ExportMusicXml::getTenthsFromInches(double inches) const
       {
       return inches * INCH / millimeters * tenths;
       }
 
-double ExportMusicXml::getTenthsFromDots(double dots)
+double ExportMusicXml::getTenthsFromDots(double dots) const
       {
       return dots / DPMM / millimeters * tenths;
       }
index f5accae..ceee18e 100644 (file)
@@ -77,8 +77,8 @@ const static std::map<QString, QString> instrumentMapping = {
             {"bnj6",            "banjo"},
             {"bongo",           "bongos"},
             {"brthns",          "baritone-horn"}, 
-            {"brtn-c",          "bariton"},
-            {"brtn-s",          "bariton"},
+            {"brtn-c",          "baritone"},
+            {"brtn-s",          "baritone"},
             {"cbs",             "cabasa"},
             {"cello",           "violoncello"},
             {"china",           "chinese-tom-toms"},
@@ -142,7 +142,7 @@ const static std::map<QString, QString> instrumentMapping = {
             {"ride",            "ride-cymbal"},
             {"rvs-cymb",        "cymbal"},
             {"sax-alt-eb",      "alto-saxophone"},
-            {"sax-bar-eb",      "bariton-saxophone"},
+            {"sax-bar-eb",      "baritone-saxophone"},
             {"sax-bass-eb",     "bass-saxophone"},
             {"sax-ms-f",        "mezzo-soprano-saxophone"},
             {"sax-sop-bb",      "soprano-saxophone"},
diff --git a/mscore/importmxmlnoteduration.cpp b/mscore/importmxmlnoteduration.cpp
new file mode 100644 (file)
index 0000000..675e622
--- /dev/null
@@ -0,0 +1,266 @@
+//=============================================================================
+//  MuseScore
+//  Music Composition & Notation
+//
+//  Copyright (C) 2018 Werner Schweer and others
+//
+//  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 "libmscore/fraction.h"
+
+#include "importmxmllogger.h"
+#include "importmxmlnoteduration.h"
+
+namespace Ms {
+
+//---------------------------------------------------------
+//   noteTypeToFraction
+//---------------------------------------------------------
+
+/**
+ Convert MusicXML note type to fraction.
+ */
+
+static Fraction noteTypeToFraction(const QString& type)
+      {
+      if (type == "1024th")
+            return Fraction(1, 1024);
+      else if (type == "512th")
+            return Fraction(1, 512);
+      else if (type == "256th")
+            return Fraction(1, 256);
+      else if (type == "128th")
+            return Fraction(1, 128);
+      else if (type == "64th")
+            return Fraction(1, 64);
+      else if (type == "32nd")
+            return Fraction(1, 32);
+      else if (type == "16th")
+            return Fraction(1, 16);
+      else if (type == "eighth")
+            return Fraction(1, 8);
+      else if (type == "quarter")
+            return Fraction(1, 4);
+      else if (type == "half")
+            return Fraction(1, 2);
+      else if (type == "whole")
+            return Fraction(1, 1);
+      else if (type == "breve")
+            return Fraction(2, 1);
+      else if (type == "long")
+            return Fraction(4, 1);
+      else if (type == "maxima")
+            return Fraction(8, 1);
+      else
+            return Fraction(0, 0);
+      }
+
+//---------------------------------------------------------
+//   calculateFraction
+//---------------------------------------------------------
+
+/**
+ Convert note type, number of dots and actual and normal notes into a duration
+ */
+
+static Fraction calculateFraction(const QString& type, const int dots, const Fraction timeMod)
+      {
+      // type
+      Fraction f = noteTypeToFraction(type);
+      if (f.isValid()) {
+            // dot(s)
+            Fraction f_no_dots = f;
+            for (int i = 0; i < dots; ++i)
+                  f += (f_no_dots / (2 << i));
+            // tuplet
+            if (timeMod.isValid())
+                  f *= timeMod;
+            // clean up (just in case)
+            f.reduce();
+            }
+      return f;
+      }
+
+//---------------------------------------------------------
+//   checkTiming
+//---------------------------------------------------------
+
+/**
+ Do timing error checks.
+ Return empty string if OK, message in case of error.
+ */
+
+QString mxmlNoteDuration::checkTiming(const QString& type, const bool rest, const bool grace)
+      {
+      //qDebug("type %s rest %d grace %d", qPrintable(type), rest, grace);
+      QString errorStr;
+
+      // normalize duration
+      if (_dura.isValid())
+            _dura.reduce();
+
+      const auto calcDura = calculateFraction(type, _dots, _timeMod);
+      if (_dura.isValid() && calcDura.isValid()) {
+            if (_dura != calcDura) {
+                  errorStr = QString("calculated duration (%1) not equal to specified duration (%2)")
+                        .arg(calcDura.print()).arg(_dura.print());
+                  //qDebug("rest %d type '%s' timemod %s", rest, qPrintable(type), qPrintable(_timeMod.print()));
+
+                  if (rest && type == "whole" && _dura.isValid()) {
+                        // Sibelius whole measure rest (not an error)
+                        errorStr = "";
+                        }
+                  else if (grace && _dura == Fraction(0, 1)) {
+                        // grace note (not an error)
+                        errorStr = "";
+                        }
+                  else {
+                        const int maxDiff = 3;       // maximum difference considered a rounding error
+                        if (qAbs(calcDura.ticks() - _dura.ticks()) <= maxDiff) {
+                              errorStr += " -> assuming rounding error";
+                              _dura = calcDura;
+                              }
+                        }
+
+                  // Special case:
+                  // Encore generates rests in tuplets w/o <tuplet> or <time-modification>.
+                  // Detect this by comparing the actual duration with the expected duration
+                  // based on note type. If actual is 2/3 of expected, the rest is part
+                  // of a tuplet.
+                  if (rest && !_timeMod.isValid()) {
+                        if (2 * calcDura.ticks() == 3 * _dura.ticks()) {
+                              _timeMod = Fraction(2, 3);
+                              errorStr += " -> assuming triplet";
+                              }
+                        }
+                  }
+            }
+      else if (_dura.isValid()) {
+            // do not report an error for typeless (whole measure) rests
+            if (!(rest && type == ""))
+                  errorStr = "calculated duration invalid, using specified duration";
+            }
+      else if (calcDura.isValid()) {
+            if (!grace) {
+                  errorStr = "specified duration invalid, using calculated duration";
+                  _dura = calcDura;       // overrule dura
+                  }
+            }
+      else {
+            errorStr = "calculated and specified duration invalid, using 4/4";
+            _dura = Fraction(4, 4);
+            }
+
+      return errorStr;
+      }
+
+//---------------------------------------------------------
+//   duration
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/duration node.
+ */
+
+void mxmlNoteDuration::duration(QXmlStreamReader& e)
+      {
+      Q_ASSERT(e.isStartElement() && e.name() == "duration");
+      _logger->logDebugTrace("MusicXMLParserPass1::duration", &e);
+
+      _dura.set(0, 0);        // invalid unless set correctly
+      int intDura = e.readElementText().toInt();
+      if (intDura > 0) {
+            if (_divs > 0) {
+                  _dura.set(intDura, 4 * _divs);
+                  _dura.reduce();       // prevent overflow in later Fraction operations
+                  }
+            else
+                  _logger->logError("illegal or uninitialized divisions", &e);
+            }
+      else
+            _logger->logError("illegal duration", &e);
+      //qDebug("duration %s valid %d", qPrintable(dura.print()), dura.isValid());
+      }
+
+//---------------------------------------------------------
+//   readProperties
+//---------------------------------------------------------
+
+/**
+ Handle selected child elements of the /score-partwise/part/measure/note/duration node.
+ Return true if handled.
+ */
+
+bool mxmlNoteDuration::readProperties(QXmlStreamReader& e)
+      {
+      const QStringRef& tag(e.name());
+      //qDebug("tag %s", qPrintable(tag.toString()));
+      if (tag == "dot") {
+            _dots++;
+            e.readNext();
+            return true;
+            }
+      else if (tag == "duration") {
+            duration(e);
+            return true;
+            }
+      else if (tag == "time-modification") {
+            timeModification(e);
+            return true;
+            }
+      return false;
+      }
+
+//---------------------------------------------------------
+//   timeModification
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/time-modification node.
+ */
+
+void mxmlNoteDuration::timeModification(QXmlStreamReader& e)
+      {
+      Q_ASSERT(e.isStartElement() && e.name() == "time-modification");
+      _logger->logDebugTrace("MusicXMLParserPass1::timeModification", &e);
+
+      int intActual = 0;
+      int intNormal = 0;
+      QString strActual;
+      QString strNormal;
+
+      while (e.readNextStartElement()) {
+            const QStringRef& tag(e.name());
+            if (tag == "actual-notes")
+                  strActual = e.readElementText();
+            else if (tag == "normal-notes")
+                  strNormal = e.readElementText();
+            else if (tag == "normal-type") {
+                  // "measure" is not a valid normal-type,
+                  // but would be accepted by setType()
+                  QString strNormalType = e.readElementText();
+                  if (strNormalType != "measure")
+                        _normalType.setType(strNormalType);
+                  }
+            else {
+                  _logger->logDebugInfo(QString("skipping '%1'").arg(e.name().toString()), &e);
+                  e.skipCurrentElement();
+                  }
+            }
+
+      intActual = strActual.toInt();
+      intNormal = strNormal.toInt();
+      if (intActual > 0 && intNormal > 0)
+            _timeMod.set(intNormal, intActual);
+      else {
+            _timeMod.set(1, 1);
+            _logger->logError(QString("illegal time-modification: actual-notes %1 normal-notes %2")
+                              .arg(strActual).arg(strNormal), &e);
+            }
+      }
+
+}
diff --git a/mscore/importmxmlnoteduration.h b/mscore/importmxmlnoteduration.h
new file mode 100644 (file)
index 0000000..80d41ba
--- /dev/null
@@ -0,0 +1,55 @@
+//=============================================================================
+//  MuseScore
+//  Music Composition & Notation
+//
+//  Copyright (C) 2018 Werner Schweer and others
+//
+//  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
+//=============================================================================
+
+#ifndef __IMPORTMXMLNOTEDURATION_H__
+#define __IMPORTMXMLNOTEDURATION_H__
+
+#include "libmscore/durationtype.h"
+#include "libmscore/fraction.h"
+
+namespace Ms {
+
+class MxmlLogger;
+
+//---------------------------------------------------------
+//   mxmlNoteDuration
+//---------------------------------------------------------
+
+/**
+ Parse the note time related part of the /score-partwise/part/measure/note node.
+ */
+
+class mxmlNoteDuration
+      {
+public:
+      mxmlNoteDuration(int divs, MxmlLogger* logger) : _divs(divs), _logger(logger) { /* nothing so far */ }
+      QString checkTiming(const QString& type, const bool rest, const bool grace);
+      Fraction dura() const { return _dura; }
+      int dots() const { return _dots; }
+      TDuration normalType() const { return _normalType; }
+      bool readProperties(QXmlStreamReader& e);
+      Fraction timeMod() const { return _timeMod; }
+
+private:
+      void duration(QXmlStreamReader& e);
+      void timeModification(QXmlStreamReader& e);
+      const int _divs;                                // the current divisions value
+      int _dots = 0;
+      Fraction _dura;
+      TDuration _normalType;
+      Fraction _timeMod { 0, 0 };                     // invalid (will handle "present but incorrect" as "not present")
+      MxmlLogger* _logger;                            ///< Error logger
+      };
+
+} // namespace Ms
+
+#endif
diff --git a/mscore/importmxmlnotepitch.cpp b/mscore/importmxmlnotepitch.cpp
new file mode 100644 (file)
index 0000000..77219c3
--- /dev/null
@@ -0,0 +1,178 @@
+//=============================================================================
+//  MuseScore
+//  Music Composition & Notation
+//
+//  Copyright (C) 2018 Werner Schweer and others
+//
+//  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 "importmxmllogger.h"
+#include "importmxmlnotepitch.h"
+#include "musicxmlsupport.h"
+
+namespace Ms {
+
+//---------------------------------------------------------
+//   accidental
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/accidental node.
+ Return the result as an Accidental.
+ */
+
+// TODO: split in reading parameters versus creation
+
+static Accidental* accidental(QXmlStreamReader& e, Score* score)
+      {
+      Q_ASSERT(e.isStartElement() && e.name() == "accidental");
+
+      bool cautionary = e.attributes().value("cautionary") == "yes";
+      bool editorial = e.attributes().value("editorial") == "yes";
+      bool parentheses = e.attributes().value("parentheses") == "yes";
+
+      const auto s = e.readElementText();
+      const auto type = mxmlString2accidentalType(s);
+
+      if (type != AccidentalType::NONE) {
+            auto a = new Accidental(score);
+            a->setAccidentalType(type);
+            if (editorial || cautionary || parentheses) {
+                  a->setHasBracket(cautionary || parentheses);
+                  a->setRole(AccidentalRole::USER);
+                  }
+            return a;
+            }
+
+      return 0;
+      }
+
+//---------------------------------------------------------
+//   displayStepOctave
+//---------------------------------------------------------
+
+/**
+ Handle <display-step> and <display-octave> for <rest> and <unpitched>
+ */
+
+void mxmlNotePitch::displayStepOctave(QXmlStreamReader& e)
+      {
+      Q_ASSERT(e.isStartElement()
+               && (e.name() == "rest" || e.name() == "unpitched"));
+
+      while (e.readNextStartElement()) {
+            if (e.name() == "display-step") {
+                  const auto step = e.readElementText();
+                  int pos = QString("CDEFGAB").indexOf(step);
+                  if (step.size() == 1 && pos >=0 && pos < 7)
+                        _displayStep = pos;
+                  else
+                        //logError(QString("invalid step '%1'").arg(strStep));
+                        qDebug("invalid step '%s'", qPrintable(step));        // TODO
+                  }
+            else if (e.name() == "display-octave") {
+                  const auto oct = e.readElementText();
+                  bool ok;
+                  _displayOctave = oct.toInt(&ok);
+                  if (!ok || _displayOctave < 0 || _displayOctave > 9) {
+                        //logError(QString("invalid octave '%1'").arg(strOct));
+                        qDebug("invalid octave '%s'", qPrintable(oct));       // TODO
+                        _displayOctave = -1;
+                        }
+                  }
+            else
+                  e.skipCurrentElement();                   // TODO log
+            }
+      }
+
+//---------------------------------------------------------
+//   pitch
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/pitch node.
+ */
+
+void mxmlNotePitch::pitch(QXmlStreamReader& e)
+      {
+      Q_ASSERT(e.isStartElement() && e.name() == "pitch");
+
+      // defaults
+      _step = -1;
+      _alter = 0;
+      _octave = -1;
+
+      while (e.readNextStartElement()) {
+            if (e.name() == "alter") {
+                  const auto alter = e.readElementText();
+                  bool ok;
+                  _alter = MxmlSupport::stringToInt(alter, &ok);       // fractions not supported by mscore
+                  if (!ok || _alter < -2 || _alter > 2) {
+                        _logger->logError(QString("invalid alter '%1'").arg(alter), &e);
+                        bool ok2;
+                        const auto altervalue = alter.toDouble(&ok2);
+                        if (ok2 && (qAbs(altervalue) < 2.0) && (_accType == AccidentalType::NONE)) {
+                              // try to see if a microtonal accidental is needed
+                              _accType = microtonalGuess(altervalue);
+                              }
+                        _alter = 0;
+                        }
+                  }
+            else if (e.name() == "octave") {
+                  const auto oct = e.readElementText();
+                  bool ok;
+                  _octave = oct.toInt(&ok);
+                  if (!ok || _octave < 0 || _octave > 9) {
+                        _logger->logError(QString("invalid octave '%1'").arg(oct), &e);
+                        _octave = -1;
+                        }
+                  }
+            else if (e.name() == "step") {
+                  const auto step = e.readElementText();
+                  const auto pos = QString("CDEFGAB").indexOf(step);
+                  if (step.size() == 1 && pos >=0 && pos < 7)
+                        _step = pos;
+                  else
+                        _logger->logError(QString("invalid step '%1'").arg(step), &e);
+                  }
+            else {
+                  ;       // TODO skipLogCurrElem();
+                  }
+            }
+      //qDebug("pitch step %d alter %d oct %d accid %hhd", step, alter, oct, accid);
+      }
+
+//---------------------------------------------------------
+//   readProperties
+//---------------------------------------------------------
+
+/**
+ Handle selected child elements of the /score-partwise/part/measure/note node.
+ Return true if handled.
+ */
+
+bool mxmlNotePitch::readProperties(QXmlStreamReader& e, Score* score)
+      {
+      const QStringRef& tag(e.name());
+
+      if (tag == "accidental") {
+            _acc = accidental(e, score);
+            return true;
+            }
+      else if (tag == "unpitched") {
+            _unpitched = true;
+            displayStepOctave(e);
+            return true;
+            }
+      else if (tag == "pitch") {
+            pitch(e);
+            return true;
+            }
+      return false;
+      }
+
+}
diff --git a/mscore/importmxmlnotepitch.h b/mscore/importmxmlnotepitch.h
new file mode 100644 (file)
index 0000000..5f1986f
--- /dev/null
@@ -0,0 +1,62 @@
+//=============================================================================
+//  MuseScore
+//  Music Composition & Notation
+//
+//  Copyright (C) 2018 Werner Schweer and others
+//
+//  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
+//=============================================================================
+
+#ifndef __IMPORTMXMLNOTEPITCH_H__
+#define __IMPORTMXMLNOTEPITCH_H__
+
+#include "libmscore/accidental.h"
+
+namespace Ms {
+
+class MxmlLogger;
+class Score;
+
+//---------------------------------------------------------
+//   mxmlNotePitch
+//---------------------------------------------------------
+
+/**
+ Parse the note pitch related part of the /score-partwise/part/measure/note node.
+ */
+
+class mxmlNotePitch
+      {
+public:
+      mxmlNotePitch(MxmlLogger* logger) : _logger(logger) { /* nothing so far */ }
+      void pitch(QXmlStreamReader& e);
+      bool readProperties(QXmlStreamReader& e, Score* score);
+      Accidental* acc() const { return _acc; }
+      AccidentalType accType() const { return _accType; }
+      int alter() const { return _alter; }
+      int displayOctave() const { return _displayOctave; }
+      int displayStep() const { return _displayStep; }
+      void displayStepOctave(QXmlStreamReader& e);
+      int octave() const { return _octave; }
+      int step() const { return _step; }
+      bool unpitched() const { return _unpitched; }
+
+private:
+      Accidental* _acc = 0;                                 // created based on accidental element
+      AccidentalType _accType = AccidentalType::NONE;       // set by pitch() based on alter value (can be microtonal)
+      int _alter = 0;
+      int _displayStep = -1;                                // invalid
+      int _displayOctave = -1;                              // invalid
+      int _octave = -1;
+      int _step = 0;
+      bool _unpitched = false;
+      MxmlLogger* _logger;                                  ///< Error logger
+      };
+
+
+} // namespace Ms
+
+#endif
index e52bbae..dc006e3 100644 (file)
@@ -29,6 +29,7 @@
 #include "libmscore/timesig.h"
 
 #include "importmxmllogger.h"
+#include "importmxmlnoteduration.h"
 #include "importmxmlpass1.h"
 #include "importmxmlpass2.h"
 #include "preferences.h"
 namespace Ms {
 
 //---------------------------------------------------------
-//   noteTypeToFraction
-//---------------------------------------------------------
-
-/**
- Convert MusicXML note type to fraction.
- */
-
-static Fraction noteTypeToFraction(const QString& type)
-      {
-      if (type == "1024th")
-            return Fraction(1, 1024);
-      else if (type == "512th")
-            return Fraction(1, 512);
-      else if (type == "256th")
-            return Fraction(1, 256);
-      else if (type == "128th")
-            return Fraction(1, 128);
-      else if (type == "64th")
-            return Fraction(1, 64);
-      else if (type == "32nd")
-            return Fraction(1, 32);
-      else if (type == "16th")
-            return Fraction(1, 16);
-      else if (type == "eighth")
-            return Fraction(1, 8);
-      else if (type == "quarter")
-            return Fraction(1, 4);
-      else if (type == "half")
-            return Fraction(1, 2);
-      else if (type == "whole")
-            return Fraction(1, 1);
-      else if (type == "breve")
-            return Fraction(2, 1);
-      else if (type == "long")
-            return Fraction(4, 1);
-      else if (type == "maxima")
-            return Fraction(8, 1);
-      else
-            return Fraction(0, 0);
-      }
-
-//---------------------------------------------------------
-//   calculateFraction
-//---------------------------------------------------------
-
-/**
- Convert note type, number of dots and actual and normal notes into a duration
- */
-
-static Fraction calculateFraction(const QString& type, const int dots, const Fraction timeMod)
-      {
-      // type
-      Fraction f = noteTypeToFraction(type);
-      if (f.isValid()) {
-            // dot(s)
-            Fraction f_no_dots = f;
-            for (int i = 0; i < dots; ++i)
-                  f += (f_no_dots / (2 << i));
-            // tuplet
-            if (timeMod.isValid())
-                  f *= timeMod;
-            // clean up (just in case)
-            f.reduce();
-            }
-      return f;
-      }
-
-//---------------------------------------------------------
 //   allocateStaves
 //---------------------------------------------------------
 
@@ -799,6 +732,30 @@ static void doCredits(Score* score, const CreditWordsList& credits, const int pa
       }
 
 //---------------------------------------------------------
+//   fixupSigmap
+//---------------------------------------------------------
+
+/**
+ To enable error handling in pass2, ensure sigmap contains a valid entry at tick = 0.
+ Required by TimeSigMap::tickValues(), called (indirectly) by Segment::add().
+ */
+
+static void fixupSigmap(MxmlLogger* logger, Score* score, const QVector<Fraction>& measureLength)
+      {
+      auto it = score->sigmap()->find(0);
+
+      if (it == score->sigmap()->end()) {
+            // no valid timesig at tick = 0
+            logger->logDebugInfo("no valid time signature at tick = 0");
+            // use length of first measure instead time signature.
+            // if there is no first measure, we probably don't care,
+            // but set a default anyway.
+            Fraction tsig = measureLength.isEmpty() ? Fraction(4, 4) : measureLength.at(0);
+            score->sigmap()->add(0, tsig);
+            }
+      }
+
+//---------------------------------------------------------
 //   parse
 //---------------------------------------------------------
 
@@ -811,14 +768,18 @@ Score::FileError MusicXMLParserPass1::parse(QIODevice* device)
       _logger->logDebugTrace("MusicXMLParserPass1::parse device");
       _parts.clear();
       _e.setDevice(device);
-      Score::FileError res = parse();
+      auto res = parse();
       if (res != Score::FileError::FILE_NO_ERROR)
             return res;
 
       // Determine the start tick of each measure in the part
       determineMeasureLength(_measureLength);
       determineMeasureStart(_measureLength, _measureStart);
+      // Fixup timesig at tick = 0 if necessary
+      fixupSigmap(_logger, _score, _measureLength);
+      // Create the measures
       createMeasures(_score, _measureLength, _measureStart);
+
       return res;
       }
 
@@ -1215,7 +1176,7 @@ void MusicXMLParserPass1::credit(CreditWordsList& credits)
                         crwords += nextPartOfFormattedString(_e);
                         }
                   else if (_e.name() == "credit-type")
-                        skipLogCurrElem();
+                        _e.skipCurrentElement();  // skip but don't log
                   else
                         skipLogCurrElem();
                   }
@@ -1225,7 +1186,7 @@ void MusicXMLParserPass1::credit(CreditWordsList& credits)
                   }
             }
       else
-            skipLogCurrElem();
+            _e.skipCurrentElement();  // skip but don't log
 
       Q_ASSERT(_e.isEndElement() && _e.name() == "credit");
       }
@@ -1308,7 +1269,7 @@ static void updateStyles(Score* score,
 void MusicXMLParserPass1::defaults(int& pageWidth, int& pageHeight)
       {
       Q_ASSERT(_e.isStartElement() && _e.name() == "defaults");
-      _logger->logDebugTrace("MusicXMLParserPass1::defaults", &_e);
+      //_logger->logDebugTrace("MusicXMLParserPass1::defaults", &_e);
 
       double millimeter = _score->spatium()/10.0;
       double tenths = 1.0;
@@ -1318,7 +1279,9 @@ void MusicXMLParserPass1::defaults(int& pageWidth, int& pageHeight)
       QString wordFontSize;
 
       while (_e.readNextStartElement()) {
-            if (_e.name() == "scaling") {
+            if (_e.name() == "appearance")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "scaling") {
                   while (_e.readNextStartElement()) {
                         if (_e.name() == "millimeters")
                               millimeter = _e.readElementText().toDouble();
@@ -1339,17 +1302,19 @@ void MusicXMLParserPass1::defaults(int& pageWidth, int& pageHeight)
                   }
             else if (_e.name() == "system-layout") {
                   while (_e.readNextStartElement()) {
-                        if (_e.name() == "system-margins")
-                              skipLogCurrElem();
+                        if (_e.name() == "system-dividers")
+                              _e.skipCurrentElement();  // skip but don't log
+                        else if (_e.name() == "system-margins")
+                              _e.skipCurrentElement();  // skip but don't log
                         else if (_e.name() == "system-distance") {
                               Spatium val(_e.readElementText().toDouble() / 10.0);
                               if (preferences.musicxmlImportLayout) {
                                     _score->style()->set(StyleIdx::minSystemDistance, val);
-                                    qDebug("system distance %f", val.val());
+                                    //qDebug("system distance %f", val.val());
                                     }
                               }
                         else if (_e.name() == "top-system-distance")
-                              skipLogCurrElem();
+                              _e.skipCurrentElement();  // skip but don't log
                         else
                               skipLogCurrElem();
                         }
@@ -1366,7 +1331,7 @@ void MusicXMLParserPass1::defaults(int& pageWidth, int& pageHeight)
                         }
                   }
             else if (_e.name() == "music-font")
-                  skipLogCurrElem();
+                  _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "word-font") {
                   wordFontFamily = _e.attributes().value("font-family").toString();
                   wordFontSize = _e.attributes().value("font-size").toString();
@@ -1377,10 +1342,8 @@ void MusicXMLParserPass1::defaults(int& pageWidth, int& pageHeight)
                   lyricFontSize = _e.attributes().value("font-size").toString();
                   _e.skipCurrentElement();
                   }
-            else if (_e.name() == "appearance")
-                  skipLogCurrElem();
             else if (_e.name() == "lyric-language")
-                  skipLogCurrElem();
+                  _e.skipCurrentElement();  // skip but don't log
             else
                   skipLogCurrElem();
             }
@@ -1613,7 +1576,11 @@ void MusicXMLParserPass1::partGroup(const int scoreParts,
       QString type = _e.attributes().value("type").toString();
 
       while (_e.readNextStartElement()) {
-            if (_e.name() == "group-symbol")
+            if (_e.name() == "group-name")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "group-abbreviation")
+                  symbol = _e.readElementText();
+            else if (_e.name() == "group-symbol")
                   symbol = _e.readElementText();
             else if (_e.name() == "group-barline") {
                   if (_e.readElementText() == "no")
@@ -1930,7 +1897,7 @@ static Fraction measureDurationAsFraction(const Fraction length, const int tsigt
  */
 
 void MusicXMLParserPass1::measure(const QString& partId,
-                                  const Fraction time,
+                                  const Fraction cTime,
                                   Fraction& mdur,
                                   VoiceOverlapDetector& vod)
       {
@@ -1944,11 +1911,13 @@ void MusicXMLParserPass1::measure(const QString& partId,
 
       while (_e.readNextStartElement()) {
             if (_e.name() == "attributes")
-                  attributes(partId);
+                  attributes(partId, cTime + mTime);
+            else if (_e.name() == "barline")
+                  _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "note") {
                   Fraction dura;
                   // note: chord and grace note handling done in note()
-                  note(partId, time + mTime, dura, vod);
+                  note(partId, cTime + mTime, dura, vod);
                   if (dura.isValid()) {
                         mTime += dura;
                         if (mTime > mDura)
@@ -1977,7 +1946,13 @@ void MusicXMLParserPass1::measure(const QString& partId,
                         }
                   }
             else if (_e.name() == "direction")
-                  direction(partId, time + mTime);
+                  direction(partId, cTime + mTime);
+            else if (_e.name() == "harmony")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "print")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "sound")
+                  _e.skipCurrentElement();  // skip but don't log
             else
                   skipLogCurrElem();
 
@@ -2040,7 +2015,7 @@ void MusicXMLParserPass1::measure(const QString& partId,
  Parse the /score-partwise/part/measure/attributes node.
  */
 
-void MusicXMLParserPass1::attributes(const QString& partId)
+void MusicXMLParserPass1::attributes(const QString& partId, const Fraction cTime)
       {
       Q_ASSERT(_e.isStartElement() && _e.name() == "attributes");
       _logger->logDebugTrace("MusicXMLParserPass1::attributes", &_e);
@@ -2050,12 +2025,18 @@ void MusicXMLParserPass1::attributes(const QString& partId)
                   clef(partId);
             else if (_e.name() == "divisions")
                   divisions();
+            else if (_e.name() == "key")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "instruments")
+                  _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "staff-details")
-                  _e.skipCurrentElement(); // skip but don't log
+                  _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "staves")
                   staves(partId);
             else if (_e.name() == "time")
-                  time();
+                  time(cTime);
+            else if (_e.name() == "transpose")
+                  _e.skipCurrentElement();  // skip but don't log
             else
                   skipLogCurrElem();
             }
@@ -2091,7 +2072,9 @@ void MusicXMLParserPass1::clef(const QString& partId)
       StaffTypes staffType = StaffTypes::STANDARD;
 
       while (_e.readNextStartElement()) {
-            if (_e.name() == "sign") {
+            if (_e.name() == "line")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "sign") {
                   QString sign = _e.readElementText();
                   if (sign == "TAB")
                         staffType = StaffTypes::TAB_DEFAULT;
@@ -2177,7 +2160,7 @@ static bool determineTimeSig(MxmlLogger* logger, const QXmlStreamReader* const x
  Parse the /score-partwise/part/measure/attributes/time node.
  */
 
-void MusicXMLParserPass1::time()
+void MusicXMLParserPass1::time(const Fraction cTime)
       {
       Q_ASSERT(_e.isStartElement() && _e.name() == "time");
 
@@ -2201,6 +2184,7 @@ void MusicXMLParserPass1::time()
             int btp = 0;       // beat-type as integer
             if (determineTimeSig(_logger, &_e, beats, beatType, timeSymbol, st, bts, btp)) {
                   _timeSigDura = Fraction(bts, btp);
+                  _score->sigmap()->add(cTime.ticks(), _timeSigDura);
                   }
             }
       }
@@ -2453,28 +2437,31 @@ void MusicXMLParserPass1::note(const QString& partId,
 
       //float alter = 0;
       bool chord = false;
-      int dots = 0;
       bool grace = false;
       //int octave = -1;
       bool bRest = false;
       int staff = 1;
       //int step = 0;
-      Fraction timeMod(1, 1);
       QString type;
       QString voice = "1";
       QString instrId;
 
+      mxmlNoteDuration mnd(_divs, _logger);
+
       while (_e.readNextStartElement()) {
-            if (_e.name() == "chord") {
-                  chord = true;
-                  _e.readNext();
+            if (mnd.readProperties(_e)) {
+                  // element handled
                   }
-            else if (_e.name() == "dot") {
-                  dots++;
+            else if (_e.name() == "accidental")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "beam")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "chord") {
+                  chord = true;
                   _e.readNext();
                   }
-            else if (_e.name() == "duration")
-                  duration(dura);
+            else if (_e.name() == "cue")
+                  _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "grace") {
                   grace = true;
                   _e.readNext();
@@ -2483,6 +2470,12 @@ void MusicXMLParserPass1::note(const QString& partId,
                   instrId = _e.attributes().value("id").toString();
                   _e.readNext();
                   }
+            else if (_e.name() == "lyric")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "notations")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "notehead")
+                  _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "pitch")
                   _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "rest") {
@@ -2501,10 +2494,14 @@ void MusicXMLParserPass1::note(const QString& partId,
                         staff = 1;
                         }
                   }
-            else if (_e.name() == "time-modification")
-                  timeModification(timeMod);
+            else if (_e.name() == "stem")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "tie")
+                  _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "type")
                   type = _e.readElementText();
+            else if (_e.name() == "unpitched")
+                  _e.skipCurrentElement();  // skip but don't log
             else if (_e.name() == "voice")
                   voice = _e.readElementText();
             else
@@ -2532,63 +2529,10 @@ void MusicXMLParserPass1::note(const QString& partId,
       if (mustInsert)
             _parts[partId]._instrList.setInstrument(instrId, sTime);
 
-      // normalize duration
-      if (dura.isValid())
-            dura.reduce();
-
-      // timing error check(s)
-      QString errorStr;
-      Fraction calcDura = calculateFraction(type, dots, timeMod);
-      if (dura.isValid() && calcDura.isValid()) {
-            if (dura != calcDura) {
-                  errorStr = QString("calculated duration (%1) not equal to specified duration (%2)")
-                        .arg(calcDura.print()).arg(dura.print());
-
-                  if (bRest && type == "whole" && dura.isValid()) {
-                        // Sibelius whole measure rest (not an error)
-                        errorStr = "";
-                        }
-                  else if (grace && dura == Fraction(0, 1)) {
-                        // grace note (not an error)
-                        errorStr = "";
-                        }
-                  else {
-                        const int maxDiff = 3; // maximum difference considered a rounding error
-                        if (qAbs(calcDura.ticks() - dura.ticks()) <= maxDiff) {
-                              errorStr += " -> assuming rounding error";
-                              dura = calcDura;
-                              }
-                        }
-
-                  // Special case:
-                  // Encore generates rests in tuplets w/o <tuplet> or <time-modification>.
-                  // Detect this by comparing the actual duration with the expected duration
-                  // based on note type. If actual is 2/3 of expected, the rest is part
-                  // of a tuplet.
-                  if (bRest && !timeMod.isValid()) {
-                        if (2 * calcDura.ticks() == 3 * dura.ticks()) {
-                              timeMod = Fraction(2, 3);
-                              errorStr += " -> assuming triplet";
-                              }
-                        }
-                  }
-            }
-      else if (dura.isValid()) {
-            // do not report an error for typeless (whole measure) rests
-            if (!(bRest && type == ""))
-                  errorStr = "calculated duration invalid, using specified duration";
-            }
-      else if (calcDura.isValid()) {
-            if (!grace) {
-                  errorStr = "specified duration invalid, using calculated duration";
-                  dura = calcDura; // overrule dura
-                  }
-            }
-      else {
-            errorStr = "calculated and specified duration invalid, using 4/4";
-            dura = Fraction(4, 4);
-            }
-
+      // check for timing error(s) and set dura
+      // keep in this order as checkTiming() might change dura
+      auto errorStr = mnd.checkTiming(type, bRest, grace);
+      dura = mnd.dura();
       if (errorStr != "")
             _logger->logError(errorStr, &_e);
 
@@ -2779,7 +2723,12 @@ void MusicXMLParserPass1::rest()
       //_logger->logDebugTrace("MusicXMLParserPass1::rest", &_e);
 
       while (_e.readNextStartElement()) {
-            skipLogCurrElem();
+            if (_e.name() == "display-octave")
+                  _e.skipCurrentElement();  // skip but don't log
+            else if (_e.name() == "display-step")
+                  _e.skipCurrentElement();  // skip but don't log
+            else
+                  skipLogCurrElem();
             }
       }
 
index 1b0132b..4906464 100644 (file)
@@ -67,10 +67,10 @@ public:
       void scoreInstrument(const QString& partId);
       void midiInstrument(const QString& partId);
       void part();
-      void measure(const QString& partId, const Fraction time, Fraction& mdur, VoiceOverlapDetector& vod);
-      void attributes(const QString& partId);
+      void measure(const QString& partId, const Fraction cTime, Fraction& mdur, VoiceOverlapDetector& vod);
+      void attributes(const QString& partId, const Fraction cTime);
       void clef(const QString& partId);
-      void time();
+      void time(const Fraction cTime);
       void divisions();
       void staves(const QString& partId);
       void direction(const QString& partId, const Fraction cTime);
index 3a4bf57..f33a495 100644 (file)
@@ -58,6 +58,8 @@
 #include "libmscore/volta.h"
 
 #include "importmxmllogger.h"
+#include "importmxmlnoteduration.h"
+#include "importmxmlnotepitch.h"
 #include "importmxmlpass2.h"
 #include "musicxmlfonthandler.h"
 #include "musicxmlsupport.h"
@@ -164,74 +166,6 @@ void MusicXmlLyricsExtend::setExtend(const int no, const int track, const int ti
       }
 
 //---------------------------------------------------------
-//   noteTypeToFraction
-//---------------------------------------------------------
-
-/**
- Convert MusicXML note type to fraction.
- */
-
-static Fraction noteTypeToFraction(const QString& type)
-      {
-      if (type == "1024th")
-            return Fraction(1, 1024);
-      else if (type == "512th")
-            return Fraction(1, 512);
-      else if (type == "256th")
-            return Fraction(1, 256);
-      else if (type == "128th")
-            return Fraction(1, 128);
-      else if (type == "64th")
-            return Fraction(1, 64);
-      else if (type == "32nd")
-            return Fraction(1, 32);
-      else if (type == "16th")
-            return Fraction(1, 16);
-      else if (type == "eighth")
-            return Fraction(1, 8);
-      else if (type == "quarter")
-            return Fraction(1, 4);
-      else if (type == "half")
-            return Fraction(1, 2);
-      else if (type == "whole")
-            return Fraction(1, 1);
-      else if (type == "breve")
-            return Fraction(2, 1);
-      else if (type == "long")
-            return Fraction(4, 1);
-      else if (type == "maxima")
-            return Fraction(8, 1);
-      else
-            return Fraction(0, 0);
-      }
-
-//---------------------------------------------------------
-//   calculateFraction
-//---------------------------------------------------------
-
-/**
- Convert note type, number of dots and actual and normal notes into a duration
- */
-
-static Fraction calculateFraction(const QString& type, const int dots, const Fraction timeMod)
-      {
-      // type
-      Fraction f = noteTypeToFraction(type);
-      if (f.isValid()) {
-            // dot(s)
-            Fraction f_no_dots = f;
-            for (int i = 0; i < dots; ++i)
-                  f += (f_no_dots / (2 << i));
-            // tuplet
-            if (timeMod.isValid())
-                  f *= timeMod;
-            // clean up (just in case)
-            f.reduce();
-            }
-      return f;
-      }
-
-//---------------------------------------------------------
 //   MusicXMLStepAltOct2Pitch
 //---------------------------------------------------------
 
@@ -1509,7 +1443,7 @@ void MusicXMLParserPass2::initPartState(const QString& partId)
       _lastVolta = 0;
       _hasDrumset = false;
       for (int i = 0; i < MAX_NUMBER_LEVEL; ++i)
-            _slur[i] = SlurDesc();
+            _slurs[i] = SlurDesc();
       for (int i = 0; i < MAX_BRACKETS; ++i)
             _brackets[i] = 0;
       for (int i = 0; i < MAX_DASHES; ++i)
@@ -3395,57 +3329,6 @@ void MusicXMLParserPass2::doEnding(const QString& partId, Measure* measure,
       }
 
 //---------------------------------------------------------
-//   isAppr
-//---------------------------------------------------------
-
-/**
- Check if v approximately equals ref.
- Used to prevent floating point comparison for equality from failing
- */
-
-static bool isAppr(const double v, const double ref, const double epsilon)
-      {
-      return v > ref - epsilon && v < ref + epsilon;
-      }
-
-//---------------------------------------------------------
-//   microtonalGuess
-//---------------------------------------------------------
-
-/**
- Convert a MusicXML alter tag into a microtonal accidental in MuseScore enum AccidentalType.
- Works only for quarter tone, half tone, three-quarters tone and whole tone accidentals.
- */
-
-static AccidentalType microtonalGuess(double val)
-      {
-      const double eps = 0.001;
-      if (isAppr(val, -2, eps))
-            return AccidentalType::FLAT2;
-      else if (isAppr(val, -1.5, eps))
-            return AccidentalType::MIRRORED_FLAT2;
-      else if (isAppr(val, -1, eps))
-            return AccidentalType::FLAT;
-      else if (isAppr(val, -0.5, eps))
-            return AccidentalType::MIRRORED_FLAT;
-      else if (isAppr(val, 0, eps))
-            return AccidentalType::NATURAL;
-      else if (isAppr(val, 0.5, eps))
-            return AccidentalType::SHARP_SLASH;
-      else if (isAppr(val, 1, eps))
-            return AccidentalType::SHARP;
-      else if (isAppr(val, 1.5, eps))
-            return AccidentalType::SHARP_SLASH4;
-      else if (isAppr(val, 2, eps))
-            return AccidentalType::SHARP2;
-      else
-            qDebug("Guess for microtonal accidental corresponding to value %f failed.", val);  // TODO
-
-      // default
-      return AccidentalType::NONE;
-      }
-
-//---------------------------------------------------------
 //   addSymToSig
 //---------------------------------------------------------
 
@@ -3842,11 +3725,7 @@ void MusicXMLParserPass2::time(const QString& partId, Measure* measure, const in
             int btp = 0; // beat-type as integer
             if (determineTimeSig(beats, beatType, timeSymbol, st, bts, btp)) {
                   _timeSigDura = Fraction(bts, btp);
-                  // TODO: verify if fractionTSig handling must be copied from DOM parser
                   Fraction fractionTSig = Fraction(bts, btp);
-                  _score->sigmap()->add(tick, fractionTSig);
-                  //Part* part = score->staff(staff)->part();
-                  //int staves = part->nstaves();
                   for (int i = 0; i < _pass1.getPart(partId)->nstaves(); ++i) {
                         TimeSig* timesig = new TimeSig(_score);
                         timesig->setVisible(printObject);
@@ -4162,42 +4041,60 @@ static void handleDisplayStep(ChordRest* cr, int step, int octave, int tick, qre
       }
 
 //---------------------------------------------------------
-//   displayStepOctave
+//   setNoteHead
 //---------------------------------------------------------
 
 /**
- Handle <display-step> and <display-octave> for <rest> and <unpitched>
+ Set the notehead parameters.
  */
 
-static void displayStepOctave(QXmlStreamReader& e,
-                              int& step,
-                              int& oct)
+static void setNoteHead(Note* note, const QColor noteheadColor, const bool noteheadParentheses, const QString& noteheadFilled)
       {
-      Q_ASSERT(e.isStartElement()
-               && (e.name() == "rest" || e.name() == "unpitched"));
+      const auto score = note->score();
 
-      while (e.readNextStartElement()) {
-            if (e.name() == "display-step") {
-                  QString strStep = e.readElementText();
-                  int pos = QString("CDEFGAB").indexOf(strStep);
-                  if (strStep.size() == 1 && pos >=0 && pos < 7)
-                        step = pos;
-                  else
-                        //logError(QString("invalid step '%1'").arg(strStep));
-                        qDebug("invalid step '%s'", qPrintable(strStep));  // TODO
-                  }
-            else if (e.name() == "display-octave") {
-                  QString strOct = e.readElementText();
-                  bool ok;
-                  oct = strOct.toInt(&ok);
-                  if (!ok || oct < 0 || oct > 9) {
-                        //logError(QString("invalid octave '%1'").arg(strOct));
-                        qDebug("invalid octave '%s'", qPrintable(strOct)); // TODO
-                        oct = -1;
-                        }
+      if (noteheadColor != QColor::Invalid)
+            note->setColor(noteheadColor);
+      if (noteheadParentheses) {
+            auto s = new Symbol(score);
+            s->setSym(SymId::noteheadParenthesisLeft);
+            s->setParent(note);
+            score->addElement(s);
+            s = new Symbol(score);
+            s->setSym(SymId::noteheadParenthesisRight);
+            s->setParent(note);
+            score->addElement(s);
+            }
+
+      if (noteheadFilled == "no")
+            note->setHeadType(NoteHead::Type::HEAD_HALF);
+      else if (noteheadFilled == "yes")
+            note->setHeadType(NoteHead::Type::HEAD_QUARTER);
+      }
+
+//---------------------------------------------------------
+//   addFiguredBassElemens
+//---------------------------------------------------------
+
+/**
+ Add the figured bass elements.
+ */
+
+static void addFiguredBassElemens(FiguredBassList& fbl, const Fraction noteStartTime, const int msTrack,
+                                  const Fraction dura, Measure* measure)
+      {
+      if (!fbl.isEmpty()) {
+            auto sTick = noteStartTime.ticks();                    // starting tick
+            foreach (FiguredBass* fb, fbl) {
+                  fb->setTrack(msTrack);
+                  // No duration tag defaults ticks() to 0; set to note value
+                  if (fb->ticks() == 0)
+                        fb->setTicks(dura.ticks());
+                  // TODO: set correct onNote value
+                  Segment* s = measure->getSegment(Segment::Type::ChordRest, sTick);
+                  s->add(fb);
+                  sTick += fb->ticks();
                   }
-            else
-                  e.skipCurrentElement();             // TODO log
+            fbl.clear();
             }
       }
 
@@ -4229,21 +4126,14 @@ Note* MusicXMLParserPass2::note(const QString& partId,
             return 0;
             }
 
-      int alter = 0;
       bool chord = false;
       bool cue = false;
       bool small = false;
-      int dots = 0;
       bool grace = false;
-      int octave = -1;
-      bool bRest = false;
+      bool rest = false;
       int staff = 1;
-      int step = 0;
-      Fraction timeMod(0, 0); // invalid (will handle "present but incorrect" as "not present")
       QString type;
       QString voice;
-      AccidentalType accType = AccidentalType::NONE; // set based on alter value (can be microtonal)
-      Accidental* acc = 0;                               // created based on accidental element
       MScore::Direction stemDir = MScore::Direction::AUTO;
       bool noStem = false;
       NoteHead::Group headGroup = NoteHead::Group::HEAD_NORMAL;
@@ -4255,17 +4145,19 @@ Note* MusicXMLParserPass2::note(const QString& partId,
       int velocity = round(_e.attributes().value("dynamics").toDouble() * 0.9);
       bool graceSlash = false;
       bool printObject = _e.attributes().value("print-object") != "no";
-      TDuration normalType;
       Beam::Mode bm  = Beam::Mode::AUTO;
-      int displayStep = -1;       // invalid
-      int displayOctave = -1; // invalid
-      bool unpitched = false;
       QString instrId;
 
+      mxmlNoteDuration mnd(_divs, _logger);
+      mxmlNotePitch mnp(_logger);
 
       while (_e.readNextStartElement() && !elementMustBePostponed(_e)) {
-            if (_e.name() == "accidental")
-                  acc = accidental();
+            if (mnp.readProperties(_e, _score)) {
+                  // element handled
+                  }
+            else if (mnd.readProperties(_e)) {
+                  // element handled
+                  }
             else if (_e.name() == "beam")
                   beam(bm);
             else if (_e.name() == "chord") {
@@ -4276,12 +4168,6 @@ Note* MusicXMLParserPass2::note(const QString& partId,
                   cue = true;
                   _e.readNext();
                   }
-            else if (_e.name() == "dot") {
-                  dots++;
-                  _e.readNext();
-                  }
-            else if (_e.name() == "duration")
-                  duration(dura);
             else if (_e.name() == "grace") {
                   grace = true;
                   graceSlash = _e.attributes().value("slash") == "yes";
@@ -4297,11 +4183,9 @@ Note* MusicXMLParserPass2::note(const QString& partId,
                   noteheadFilled = _e.attributes().value("filled").toString();
                   headGroup = convertNotehead(_e.readElementText());
                   }
-            else if (_e.name() == "pitch")
-                  pitch(step, alter, octave, accType);
             else if (_e.name() == "rest") {
-                  bRest = true;
-                  rest(displayStep, displayOctave);
+                  rest = true;
+                  mnp.displayStepOctave(_e);
                   }
             else if (_e.name() == "staff") {
                   QString strStaff = _e.readElementText();
@@ -4317,16 +4201,10 @@ Note* MusicXMLParserPass2::note(const QString& partId,
                   }
             else if (_e.name() == "stem")
                   stem(stemDir, noStem);
-            else if (_e.name() == "time-modification")
-                  timeModification(timeMod, normalType);
             else if (_e.name() == "type") {
                   small = _e.attributes().value("size") == "cue";
                   type = _e.readElementText();
                   }
-            else if (_e.name() == "unpitched") {
-                  unpitched = true;
-                  displayStepOctave(_e, displayStep, displayOctave);
-                  }
             else if (_e.name() == "voice")
                   voice = _e.readElementText();
             else
@@ -4348,81 +4226,10 @@ Note* MusicXMLParserPass2::note(const QString& partId,
       if (voice == "")
             voice = "1";
 
-      // accidental handling
-      //qDebug("note acc %p type %hhd acctype %hhd",
-      //       acc, acc ? acc->accidentalType() : static_cast<Ms::AccidentalType>(0), accType);
-      if (!acc && accType != AccidentalType::NONE) {
-            acc = new Accidental(_score);
-            acc->setAccidentalType(accType);
-            }
-
-      //_logger->logDebugInfo(e, QString("dura %1 valid %2").arg(dura.print()).arg(dura.isValid()), &_e);
-      // normalize duration
-      if (dura.isValid())
-            dura.reduce();
-
-      // timing error check(s)
-      // note that all passes must calculate the same timing and other (TODO) checks
-      QString errorStr;
-      Fraction calcDura = calculateFraction(type, dots, timeMod);
-      /*
-      _logger->logDebugInfo(e, QString("dura %1 valid %2 fraction %3 valid %4")
-                   .arg(dura.print()).arg(dura.isValid())
-                   .arg(fraction.print()).arg(fraction.isValid()),
-                   &_e
-                   );
-       */
-      bool wholeMeasureRest = isWholeMeasureRest(bRest, type, dura, Fraction::fromTicks(measure->ticks()));
-      if (dura.isValid() && calcDura.isValid()) {
-            if (dura != calcDura) {
-                  errorStr = QString("calculated duration (%1) not equal to specified duration (%2)")
-                        .arg(calcDura.print()).arg(dura.print());
-
-                  if (wholeMeasureRest) {
-                        // do not report an error for whole measure rests
-                        errorStr = "";
-                        }
-                  else if (grace && dura == Fraction(0, 1)) {
-                        // grace note (not an error)
-                        errorStr = "";
-                        }
-                  else {
-                        const int maxDiff = 3; // maximum difference considered a rounding error
-                        if (qAbs(calcDura.ticks() - dura.ticks()) <= maxDiff) {
-                              errorStr += " -> assuming rounding error";
-                              dura = calcDura;
-                              }
-                        }
-
-                  // Special case:
-                  // Encore generates rests in tuplets w/o <tuplet> or <time-modification>.
-                  // Detect this by comparing the actual duration with the expected duration
-                  // based on note type. If actual is 2/3 of expected, the rest is part
-                  // of a tuplet.
-                  if (bRest && !timeMod.isValid()) {
-                        if (2 * calcDura.ticks() == 3 * dura.ticks()) {
-                              timeMod = Fraction(2, 3);
-                              errorStr += " -> assuming triplet";
-                              }
-                        }
-                  }
-            }
-      else if (dura.isValid()) {
-            // do not report an error for typeless (whole measure) rests
-            if (!wholeMeasureRest)
-                  errorStr = QString("calculated duration invalid, using specified duration (%1)").arg(dura.print());
-            }
-      else if (calcDura.isValid()) {
-            if (!grace) {
-                  errorStr = QString("specified duration invalid, using calculated duration (%1)").arg(calcDura.print());
-                  dura = calcDura; // overrule dura
-                  }
-            }
-      else {
-            errorStr = "calculated and specified duration invalid, using 4/4";
-            dura = Fraction(4, 4);
-            }
-
+      // check for timing error(s) and set dura
+      // keep in this order as checkTiming() might change dura
+      auto errorStr = mnd.checkTiming(type, rest, grace);
+      dura = mnd.dura();
       if (errorStr != "")
             _logger->logError(errorStr, &_e);
 
@@ -4462,7 +4269,7 @@ Note* MusicXMLParserPass2::note(const QString& partId,
       else {
             }
 
-      TDuration duration = determineDuration(bRest, type, dots, dura, Fraction::fromTicks(measure->ticks()));
+      TDuration duration = determineDuration(rest, type, mnd.dots(), dura, Fraction::fromTicks(measure->ticks()));
 
       ChordRest* cr = 0;
       Note* note = 0;
@@ -4472,7 +4279,7 @@ Note* MusicXMLParserPass2::note(const QString& partId,
       // - prevTime for others
       const Fraction noteStartTime = chord ? prevSTime : sTime;
 
-      if (bRest) {
+      if (rest) {
             int track = msTrack + msVoice;
             cr = addRest(_score, measure, noteStartTime.ticks(), track, msMove,
                          duration, dura);
@@ -4489,9 +4296,9 @@ Note* MusicXMLParserPass2::note(const QString& partId,
                         cr->setBeamMode(Beam::Mode::NONE);
                   cr->setSmall(small);
                   if (noteColor != QColor::Invalid)
-                      cr->setColor(noteColor);
+                        cr->setColor(noteColor);
                   cr->setVisible(printObject);
-                  handleDisplayStep(cr, displayStep, displayOctave, noteStartTime.ticks(), _score->spatium());
+                  handleDisplayStep(cr, mnp.displayStep(), mnp.displayOctave(), noteStartTime.ticks(), _score->spatium());
                   }
             }
       else {
@@ -4542,34 +4349,17 @@ Note* MusicXMLParserPass2::note(const QString& partId,
             note->setSmall(small);
             note->setHeadGroup(headGroup);
             if (noteColor != QColor::Invalid)
-                note->setColor(noteColor);
-            else if (noteheadColor != QColor::Invalid)
-                note->setColor(noteheadColor);
+                  note->setColor(noteColor);
+            setNoteHead(note, noteheadColor, noteheadParentheses, noteheadFilled);
             note->setVisible(printObject); // TODO also set the stem to invisible
 
-            if (noteheadParentheses) {
-                  Symbol* s = new Symbol(_score);
-                  s->setSym(SymId::noteheadParenthesisLeft);
-                  s->setParent(note);
-                  _score->addElement(s);
-                  s = new Symbol(_score);
-                  s->setSym(SymId::noteheadParenthesisRight);
-                  s->setParent(note);
-                  _score->addElement(s);
-                  }
-
-            if (noteheadFilled == "no")
-                  note->setHeadType(NoteHead::Type::HEAD_HALF);
-            else if (noteheadFilled == "yes")
-                  note->setHeadType(NoteHead::Type::HEAD_QUARTER);
-
             if (velocity > 0) {
                   note->setVeloType(Note::ValueType::USER_VAL);
                   note->setVeloOffset(velocity);
                   }
 
             const MusicXMLDrumset& mxmlDrumset = _pass1.getDrumset(partId);
-            if (unpitched) {
+            if (mnp.unpitched()) {
                   //&& drumsets.contains(partId)
                   if (_hasDrumset
                       && mxmlDrumset.contains(instrId)) {
@@ -4582,13 +4372,13 @@ Note* MusicXMLParserPass2::note(const QString& partId,
                         }
                   else {
                         //qDebug("disp step %d oct %d", displayStep, displayOctave);
-                        xmlSetPitch(note, displayStep, 0, displayOctave, 0, _pass1.getPart(partId)->instrument());
+                        xmlSetPitch(note, mnp.displayStep(), 0, mnp.displayOctave(), 0, _pass1.getPart(partId)->instrument());
                         }
                   }
             else {
                   int ottavaStaff = (msTrack - _pass1.trackForPart(partId)) / VOICES;
                   int octaveShift = _pass1.octaveShift(partId, ottavaStaff, noteStartTime);
-                  xmlSetPitch(note, step, alter, octave, octaveShift, _pass1.getPart(partId)->instrument());
+                  xmlSetPitch(note, mnp.step(), mnp.alter(), mnp.octave(), octaveShift, _pass1.getPart(partId)->instrument());
                   }
 
             // set drumset information
@@ -4596,11 +4386,11 @@ Note* MusicXMLParserPass2::note(const QString& partId,
             // line and stem direction, while a MusicXML file contains actuals.
             // the MusicXML values for each note are simply copied to the defaults
 
-            if (unpitched) {
+            if (mnp.unpitched()) {
                   // determine staff line based on display-step / -octave and clef type
                   ClefType clef = c->staff()->clef(noteStartTime.ticks());
                   int po = ClefInfo::pitchOffset(clef);
-                  int pitch = MusicXMLStepAltOct2Pitch(displayStep, 0, displayOctave);
+                  int pitch = MusicXMLStepAltOct2Pitch(mnp.displayStep(), 0, mnp.displayOctave());
                   int line = po - absStep(pitch);
 
                   // correct for number of staff lines
@@ -4631,12 +4421,22 @@ Note* MusicXMLParserPass2::note(const QString& partId,
                   _pass1.setDrumsetDefault(partId, instrId, headGroup, line, stemDir);
                   }
 
+            // accidental handling
+            //qDebug("note acc %p type %hhd acctype %hhd",
+            //       acc, acc ? acc->accidentalType() : static_cast<Ms::AccidentalType>(0), accType);
+            Accidental* acc = mnp.acc();
+            if (!acc && mnp.accType() != AccidentalType::NONE) {
+                  acc = new Accidental(_score);
+                  acc->setAccidentalType(mnp.accType());
+                  }
+
             if (acc) {
                   note->add(acc);
                   // save alter value for user accidental
                   if (acc->accidentalType() != AccidentalType::NONE)
-                        alt = alter;
+                        alt = mnp.alter();
                   }
+
             c->add(note);
             //c->setStemDirection(stemDir); // already done in handleBeamAndStemDir()
             c->setNoStem(noStem);
@@ -4697,13 +4497,14 @@ Note* MusicXMLParserPass2::note(const QString& partId,
 
       if (!chord && !grace) {
             // do tuplet if valid time-modification is not 1/1 and is not 1/2 (tremolo)
+            auto timeMod = mnd.timeMod();
             if (timeMod.isValid() && timeMod != Fraction(1, 1) && timeMod != Fraction(1, 2)) {
                   // find part-relative track
                   Part* part = _pass1.getPart(partId);
                   Q_ASSERT(part);
                   int scoreRelStaff = _score->staffIdx(part); // zero-based number of parts first staff in the score
                   int partRelTrack = msTrack + msVoice - scoreRelStaff * VOICES;
-                  addTupletToChord(cr, _tuplets[partRelTrack], _tuplImpls[partRelTrack], timeMod, tupletDesc, normalType);
+                  addTupletToChord(cr, _tuplets[partRelTrack], _tuplImpls[partRelTrack], timeMod, tupletDesc, mnd.normalType());
                   }
             }
 
@@ -4711,27 +4512,14 @@ Note* MusicXMLParserPass2::note(const QString& partId,
       if (cr) {
             // add lyrics and stop corresponding extends
             addLyrics(_logger, &_e, cr, numberedLyrics, defaultyLyrics, unNumberedLyrics, extendedLyrics, _extendedLyrics);
-            if (bRest) {
+            if (rest) {
                   // stop all extends
                   _extendedLyrics.setExtend(-1, cr->track(), cr->tick());
                   }
             }
 
       // add figured bass element
-      if (!fbl.isEmpty()) {
-            int sTick = noteStartTime.ticks();        // starting tick
-            foreach (FiguredBass* fb, fbl) {
-                  fb->setTrack(msTrack);
-                  // No duration tag defaults ticks() to 0; set to note value
-                  if (fb->ticks() == 0)
-                        fb->setTicks(dura.ticks());
-                  // TODO: set correct onNote value
-                  Segment* s = measure->getSegment(Segment::Type::ChordRest, sTick);
-                  s->add(fb);
-                  sTick += fb->ticks();
-                  }
-            fbl.clear();
-            }
+      addFiguredBassElemens(fbl, noteStartTime, msTrack, dura, measure);
 
       // don't count chord or grace note duration
       // note that this does not check the MusicXML requirement that notes in a chord
@@ -4739,6 +4527,8 @@ Note* MusicXMLParserPass2::note(const QString& partId,
       if (chord || grace)
             dura.set(0, 1);
 
+      if (!(_e.isEndElement() && _e.name() == "note"))
+            qDebug("name %s line %lld", qPrintable(_e.name().toString()), _e.lineNumber());
       Q_ASSERT(_e.isEndElement() && _e.name() == "note");
 
       return note;
@@ -5208,39 +4998,6 @@ void MusicXMLParserPass2::harmony(const QString& partId, Measure* measure, const
       }
 
 //---------------------------------------------------------
-//   accidental
-//---------------------------------------------------------
-
-/**
- Parse the /score-partwise/part/measure/note/accidental node.
- Return the result as an Accidental.
- */
-
-Accidental* MusicXMLParserPass2::accidental()
-      {
-      Q_ASSERT(_e.isStartElement() && _e.name() == "accidental");
-
-      bool cautionary = _e.attributes().value("cautionary") == "yes";
-      bool editorial = _e.attributes().value("editorial") == "yes";
-      bool parentheses = _e.attributes().value("parentheses") == "yes";
-
-      QString s = _e.readElementText();
-      AccidentalType type = mxmlString2accidentalType(s);
-
-      if (type != AccidentalType::NONE) {
-            Accidental* a = new Accidental(_score);
-            a->setAccidentalType(type);
-            if (editorial || cautionary || parentheses) {
-                  a->setHasBracket(cautionary || parentheses);
-                  a->setRole(AccidentalRole::USER);
-                  }
-            return a;
-            }
-
-      return 0;
-      }
-
-//---------------------------------------------------------
 //   beam
 //---------------------------------------------------------
 
@@ -5319,121 +5076,6 @@ void MusicXMLParserPass2::backup(Fraction& dura)
       }
 
 //---------------------------------------------------------
-//   timeModification
-//---------------------------------------------------------
-
-/**
- Parse the /score-partwise/part/measure/note/time-modification node.
- */
-
-void MusicXMLParserPass2::timeModification(Fraction& timeMod, TDuration& normalType)
-      {
-      Q_ASSERT(_e.isStartElement() && _e.name() == "time-modification");
-
-      int intActual = 0;
-      int intNormal = 0;
-      QString strActual;
-      QString strNormal;
-
-      while (_e.readNextStartElement()) {
-            if (_e.name() == "actual-notes")
-                  strActual = _e.readElementText();
-            else if (_e.name() == "normal-notes")
-                  strNormal = _e.readElementText();
-            else if (_e.name() == "normal-type") {
-                  // "measure" is not a valid normal-type,
-                  // but would be accepted by setType()
-                  QString strNormalType = _e.readElementText();
-                  if (strNormalType != "measure")
-                        normalType.setType(strNormalType);
-                  }
-            else
-                  skipLogCurrElem();
-            }
-
-      intActual = strActual.toInt();
-      intNormal = strNormal.toInt();
-      if (intActual > 0 && intNormal > 0)
-            timeMod.set(intNormal, intActual);
-      else {
-            timeMod.set(0, 0); // invalid
-            _logger->logError(QString("illegal time-modification: actual-notes %1 normal-notes %2")
-                              .arg(strActual).arg(strNormal), &_e);
-            }
-      }
-
-//---------------------------------------------------------
-//   pitch
-//---------------------------------------------------------
-
-/**
- Parse the /score-partwise/part/measure/note/pitch node.
- */
-
-void MusicXMLParserPass2::pitch(int& step, int& alter, int& oct, AccidentalType& accid)
-      {
-      Q_ASSERT(_e.isStartElement() && _e.name() == "pitch");
-
-      // defaults
-      step = -1;
-      alter = 0;
-      oct = -1;
-
-      while (_e.readNextStartElement()) {
-            if (_e.name() == "alter") {
-                  QString strAlter = _e.readElementText();
-                  bool ok;
-                  alter = MxmlSupport::stringToInt(strAlter, &ok); // fractions not supported by mscore
-                  if (!ok || alter < -2.5 || alter > 2.5) {
-                        _logger->logError(QString("invalid alter '%1'").arg(strAlter), &_e);
-                        bool ok2;
-                        double altervalue = strAlter.toDouble(&ok2);
-                        if (ok2 && (qAbs(altervalue) < 2.0) && (accid == AccidentalType::NONE)) {
-                              // try to see if a microtonal accidental is needed
-                              accid = microtonalGuess(altervalue);
-                              }
-                        alter = 0;
-                        }
-                  }
-            else if (_e.name() == "octave") {
-                  QString strOct = _e.readElementText();
-                  bool ok;
-                  oct = strOct.toInt(&ok);
-                  if (!ok || oct < 0 || oct > 9) {
-                        _logger->logError(QString("invalid octave '%1'").arg(strOct), &_e);
-                        oct = -1;
-                        }
-                  }
-            else if (_e.name() == "step") {
-                  QString strStep = _e.readElementText();
-                  int pos = QString("CDEFGAB").indexOf(strStep);
-                  if (strStep.size() == 1 && pos >=0 && pos < 7)
-                        step = pos;
-                  else
-                        _logger->logError(QString("invalid step '%1'").arg(strStep), &_e);
-                  }
-            else
-                  skipLogCurrElem();
-            }
-      //qDebug("pitch step %d alter %d oct %d accid %hhd", step, alter, oct, accid);
-      }
-
-//---------------------------------------------------------
-//   rest
-//---------------------------------------------------------
-
-/**
- Parse the /score-partwise/part/measure/note/rest node.
- */
-
-void MusicXMLParserPass2::rest(int& step, int& octave)
-      {
-      Q_ASSERT(_e.isStartElement() && _e.name() == "rest");
-
-      displayStepOctave(_e, step, octave);
-      }
-
-//---------------------------------------------------------
 //   lyric -- parse a MusicXML lyric element
 //---------------------------------------------------------
 
@@ -5536,384 +5178,425 @@ void MusicXMLParserPass2::lyric(QMap<int, Lyrics*>& numbrdLyrics,
             l->setColor(lyricColor);
       }
 
-
 //---------------------------------------------------------
-//   notations
+//   slur
 //---------------------------------------------------------
 
-
 /**
- Parse the /score-partwise/part/measure/note/notations node.
- Note that some notations attach to notes only in MuseScore,
- which means trying to attach them to a rest will crash,
- as in that case note is 0.
+ Parse the /score-partwise/part/measure/note/notations/slur node.
  */
 
-void MusicXMLParserPass2::notations(Note* note, ChordRest* cr, const int tick,
-                                    MusicXmlTupletDesc& tupletDesc, bool& lastGraceAFter)
+void MusicXMLParserPass2::slur(ChordRest* cr, const int tick, const int track, bool& lastGraceAFter)
       {
-      Q_ASSERT(_e.isStartElement() && _e.name() == "notations");
+      Q_ASSERT(_e.isStartElement() && _e.name() == "slur");
+
+      int slurNo   = _e.attributes().value("number").toString().toInt();
+      if (slurNo > 0) slurNo--;
+      QString slurType = _e.attributes().value("type").toString();
+      QString lineType  = _e.attributes().value("line-type").toString();
+      if (lineType == "") lineType = "solid";
+
+      // PriMus Music-Notation by Columbussoft (build 10093) generates overlapping
+      // slurs that do not have a number attribute to distinguish them.
+      // The duplicates must be ignored, to prevent memory allocation issues,
+      // which caused a MuseScore crash
+      // Similar issues happen with Sibelius 7.1.3 (direct export)
+
+      if (slurType == "start") {
+            if (_slurs[slurNo].isStart())
+                  // slur start when slur already started: report error
+                  _logger->logError(QString("ignoring duplicate slur start"), &_e);
+            else if (_slurs[slurNo].isStop()) {
+                  // slur start when slur already stopped: wrap up
+                  Slur* newSlur = _slurs[slurNo].slur();
+                  newSlur->setTick(tick);
+                  newSlur->setStartElement(cr);
+                  _slurs[slurNo] = SlurDesc();
+                  }
+            else {
+                  // slur start for new slur: init
+                  Slur* newSlur = new Slur(_score);
+                  if (cr->isGrace())
+                        newSlur->setAnchor(Spanner::Anchor::CHORD);
+                  if (lineType == "dotted")
+                        newSlur->setLineType(1);
+                  else if (lineType == "dashed")
+                        newSlur->setLineType(2);
+                  newSlur->setTick(tick);
+                  newSlur->setStartElement(cr);
+                  QString pl = _e.attributes().value("placement").toString();
+                  if (pl == "above")
+                        newSlur->setSlurDirection(MScore::Direction::UP);
+                  else if (pl == "below")
+                        newSlur->setSlurDirection(MScore::Direction::DOWN);
+                  newSlur->setTrack(track);
+                  newSlur->setTrack2(track);
+                  _slurs[slurNo].start(newSlur);
+                  _score->addElement(newSlur);
+                  }
+            }
+      else if (slurType == "stop") {
+            if (_slurs[slurNo].isStart()) {
+                  // slur stop when slur already started: wrap up
+                  Slur* newSlur = _slurs[slurNo].slur();
+                  if (!(cr->isGrace())) {
+                        newSlur->setTick2(tick);
+                        newSlur->setTrack2(track);
+                        }
+                  newSlur->setEndElement(cr);
+                  _slurs[slurNo] = SlurDesc();
+                  }
+            else if (_slurs[slurNo].isStop())
+                  // slur stop when slur already stopped: report error
+                  _logger->logError(QString("ignoring duplicate slur stop"), &_e);
+            else {
+                  // slur stop for new slur: init
+                  Slur* newSlur = new Slur(_score);
+                  if (!(cr->isGrace())) {
+                        newSlur->setTick2(tick);
+                        newSlur->setTrack2(track);
+                        }
+                  newSlur->setEndElement(cr);
+                  _slurs[slurNo].stop(newSlur);
+                  }
+            // any grace note containing a slur stop means
+            // last note of a grace after set has been found
+            if (cr->isGrace())
+                  lastGraceAFter = true;
+            }
+      else if (slurType == "continue")
+            ;              // ignore
+      else
+            _logger->logError(QString("unknown slur type %1").arg(slurType), &_e);
 
-      lastGraceAFter = false;       // ensure default
+      _e.readNext();
+      }
 
-      Measure* measure = cr->measure();
-      int ticks = cr->duration().ticks();
-      int track = cr->track();
-      int trk = (track / VOICES) * VOICES; // first track of staff
+//---------------------------------------------------------
+//   tied
+//---------------------------------------------------------
 
-      QString wavyLineType;
-      int wavyLineNo = 0;
-      QString arpeggioType;
-      //      QString glissandoType;
-      int breath = -1;
-      int tremolo = 0;
-      QString tremoloType;
-      QString placement;
-      QStringList dynamics;
-      // qreal rx = 0.0;
-      // qreal ry = 0.0;
-      qreal yoffset = 0.0; // actually this is default-y
-      // qreal xoffset = 0.0; // not used
-      bool hasYoffset = false;
-      QString chordLineType;
+/**
+ Parse the /score-partwise/part/measure/note/notations/tied node.
+ */
 
-      while (_e.readNextStartElement()) {
-            if (_e.name() == "slur") {
-                  int slurNo   = _e.attributes().value("number").toString().toInt();
-                  if (slurNo > 0) slurNo--;
-                  QString slurType = _e.attributes().value("type").toString();
-                  QString lineType  = _e.attributes().value("line-type").toString();
-                  if (lineType == "") lineType = "solid";
-
-                  // PriMus Music-Notation by Columbussoft (build 10093) generates overlapping
-                  // slurs that do not have a number attribute to distinguish them.
-                  // The duplicates must be ignored, to prevent memory allocation issues,
-                  // which caused a MuseScore crash
-                  // Similar issues happen with Sibelius 7.1.3 (direct export)
-
-                  if (slurType == "start") {
-                        if (_slur[slurNo].isStart())
-                              // slur start when slur already started: report error
-                              _logger->logError(QString("ignoring duplicate slur start"), &_e);
-                        else if (_slur[slurNo].isStop()) {
-                              // slur start when slur already stopped: wrap up
-                              Slur* newSlur = _slur[slurNo].slur();
-                              newSlur->setTick(tick);
-                              newSlur->setStartElement(cr);
-                              _slur[slurNo] = SlurDesc();
-                              }
-                        else {
-                              // slur start for new slur: init
-                              Slur* newSlur = new Slur(_score);
-                              if (cr->isGrace())
-                                    newSlur->setAnchor(Spanner::Anchor::CHORD);
-                              if (lineType == "dotted")
-                                    newSlur->setLineType(1);
-                              else if (lineType == "dashed")
-                                    newSlur->setLineType(2);
-                              newSlur->setTick(tick);
-                              newSlur->setStartElement(cr);
-                              QString pl = _e.attributes().value("placement").toString();
-                              if (pl == "above")
-                                    newSlur->setSlurDirection(MScore::Direction::UP);
-                              else if (pl == "below")
-                                    newSlur->setSlurDirection(MScore::Direction::DOWN);
-                              newSlur->setTrack(track);
-                              newSlur->setTrack2(track);
-                              _slur[slurNo].start(newSlur);
-                              _score->addElement(newSlur);
-                              }
-                        }
-                  else if (slurType == "stop") {
-                        if (_slur[slurNo].isStart()) {
-                              // slur stop when slur already started: wrap up
-                              Slur* newSlur = _slur[slurNo].slur();
-                              if (!(cr->isGrace())) {
-                                    newSlur->setTick2(tick);
-                                    newSlur->setTrack2(track);
-                                    }
-                              newSlur->setEndElement(cr);
-                              _slur[slurNo] = SlurDesc();
-                              }
-                        else if (_slur[slurNo].isStop())
-                              // slur stop when slur already stopped: report error
-                              _logger->logError(QString("ignoring duplicate slur stop"), &_e);
-                        else {
-                              // slur stop for new slur: init
-                              Slur* newSlur = new Slur(_score);
-                              if (!(cr->isGrace())) {
-                                    newSlur->setTick2(tick);
-                                    newSlur->setTrack2(track);
-                                    }
-                              newSlur->setEndElement(cr);
-                              _slur[slurNo].stop(newSlur);
-                              }
-                        // any grace note containing a slur stop means
-                        // last note of a grace after set has been found
-                        if (cr->isGrace())
-                              lastGraceAFter = true;
-                        }
-                  else if (slurType == "continue")
-                        ;  // ignore
+void MusicXMLParserPass2::tied(Note* note, const int track)
+      {
+      Q_ASSERT(_e.isStartElement() && _e.name() == "tied");
+
+      QString tiedType = _e.attributes().value("type").toString();
+      if (tiedType == "start") {
+            if (_tie) {
+                  _logger->logError(QString("Tie already active"), &_e);
+                  }
+            else if (note) {
+                  _tie = new Tie(_score);
+                  note->setTieFor(_tie);
+                  _tie->setStartNote(note);
+                  _tie->setTrack(track);
+                  QString tiedOrientation = _e.attributes().value("orientation").toString();
+                  if (tiedOrientation == "over")
+                        _tie->setSlurDirection(MScore::Direction::UP);
+                  else if (tiedOrientation == "under")
+                        _tie->setSlurDirection(MScore::Direction::DOWN);
+                  else if (tiedOrientation == "auto")
+                        ;              // ignore
+                  else if (tiedOrientation == "")
+                        ;              // ignore
                   else
-                        _logger->logError(QString("unknown slur type %1").arg(slurType), &_e);
-                  _e.readNext();
+                        _logger->logError(QString("unknown tied orientation: %1").arg(tiedOrientation), &_e);
+
+                  QString lineType  = _e.attributes().value("line-type").toString();
+                  if (lineType == "dotted")
+                        _tie->setLineType(1);
+                  else if (lineType == "dashed")
+                        _tie->setLineType(2);
+                  _tie = 0;
                   }
-            else if (_e.name() == "tied") {
-                  QString tiedType = _e.attributes().value("type").toString();
-                  if (tiedType == "start") {
-                        if (_tie) {
-                              _logger->logError(QString("Tie already active"), &_e);
-                              }
-                        else if (note) {
-                              _tie = new Tie(_score);
-                              note->setTieFor(_tie);
-                              _tie->setStartNote(note);
-                              _tie->setTrack(track);
-                              QString tiedOrientation = _e.attributes().value("orientation").toString();
-                              if (tiedOrientation == "over")
-                                    _tie->setSlurDirection(MScore::Direction::UP);
-                              else if (tiedOrientation == "under")
-                                    _tie->setSlurDirection(MScore::Direction::DOWN);
-                              else if (tiedOrientation == "auto")
-                                    ;  // ignore
-                              else if (tiedOrientation == "")
-                                    ;  // ignore
-                              else
-                                    _logger->logError(QString("unknown tied orientation: %1").arg(tiedOrientation), &_e);
-
-                              QString lineType  = _e.attributes().value("line-type").toString();
-                              if (lineType == "dotted")
-                                    _tie->setLineType(1);
-                              else if (lineType == "dashed")
-                                    _tie->setLineType(2);
-                              _tie = 0;
-                              }
-                        }
-                  else if (tiedType == "stop")
-                        ;  // ignore
-                  else
-                        _logger->logError(QString("unknown tied type %").arg(tiedType), &_e);
-                  _e.readNext();
+            }
+      else if (tiedType == "stop")
+            ;              // ignore
+      else
+            _logger->logError(QString("unknown tied type %").arg(tiedType), &_e);
+
+      _e.readNext();
+      }
+
+//---------------------------------------------------------
+//   dynamics
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/notations/dynamics node.
+ */
+
+void MusicXMLParserPass2::dynamics(QString& placement, QStringList& dynamicslist)
+      {
+      Q_ASSERT(_e.isStartElement() && _e.name() == "dynamics");
+
+      placement = _e.attributes().value("placement").toString();
+      if (preferences.musicxmlImportLayout) {
+            // ry        = ee.attribute(QString("relative-y"), "0").toDouble() * -.1;
+            // rx        = ee.attribute(QString("relative-x"), "0").toDouble() * .1;
+            // yoffset   = _e.attributes().value("default-y").toDouble(&hasYoffset) * -0.1;
+            // xoffset   = ee.attribute("default-x", "0.0").toDouble() * 0.1;
+            }
+      while (_e.readNextStartElement()) {
+            if (_e.name() == "other-dynamics")
+                  dynamicslist.push_back(_e.readElementText());
+            else {
+                  dynamicslist.push_back(_e.name().toString());
+                  _e.readNext();
                   }
-            else if (_e.name() == "tuplet") {
-                  tuplet(tupletDesc);
+            }
+      }
+
+//---------------------------------------------------------
+//   articulations
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/notations/articulations node.
+ Note that some notations attach to notes only in MuseScore,
+ which means trying to attach them to a rest will crash,
+ as in that case note is 0.
+ */
+
+void MusicXMLParserPass2::articulations(ChordRest* cr, int& breath, QString& chordLineType)
+      {
+      Q_ASSERT(_e.isStartElement() && _e.name() == "articulations");
+
+      while (_e.readNextStartElement()) {
+            if (addMxmlArticulationToChord(cr, _e.name().toString())) {
+                  _e.readNext();
+                  continue;
                   }
-            else if (_e.name() == "dynamics") {
-                  placement = _e.attributes().value("placement").toString();
-                  if (preferences.musicxmlImportLayout) {
-                        // ry        = ee.attribute(QString("relative-y"), "0").toDouble() * -.1;
-                        // rx        = ee.attribute(QString("relative-x"), "0").toDouble() * .1;
-                        yoffset   = _e.attributes().value("default-y").toDouble(&hasYoffset) * -0.1;
-                        // xoffset   = ee.attribute("default-x", "0.0").toDouble() * 0.1;
-                        }
-                  while (_e.readNextStartElement()) {
-                        if (_e.name() == "other-dynamics")
-                              dynamics.push_back(_e.readElementText());
-                        else {
-                              dynamics.push_back(_e.name().toString());
-                              _e.readNext();
-                              }
-                        }
+            else if (_e.name() == "breath-mark") {
+                  breath = 0;
+                  _e.readElementText();
+                  // TODO: handle value read (note: encoding unknown, only "comma" found)
                   }
-            else if (_e.name() == "articulations") {
-                  while (_e.readNextStartElement()) {
-                        if (addMxmlArticulationToChord(cr, _e.name().toString())) {
-                              _e.readNext();
-                              continue;
-                              }
-                        else if (_e.name() == "breath-mark") {
-                              breath = 0;
-                              _e.readElementText();
-                              // TODO: handle value read (note: encoding unknown, only "comma" found)
-                              }
-                        else if (_e.name() == "caesura") {
-                              breath = 3;
-                              _e.readNext();
-                              }
-                        else if (_e.name() == "doit"
-                                 || _e.name() == "falloff"
-                                 || _e.name() == "plop"
-                                 || _e.name() == "scoop") {
-                              chordLineType = _e.name().toString();
-                              _e.readNext();
-                              }
-                        else if (_e.name() == "strong-accent") {
-                              QString strongAccentType = _e.attributes().value("type").toString();
-                              if (strongAccentType == "up" || strongAccentType == "")
-                                    addArticulationToChord(cr, ArticulationType::Marcato, "up");
-                              else if (strongAccentType == "down")
-                                    addArticulationToChord(cr, ArticulationType::Marcato, "down");
-                              else
-                                    _logger->logError(QString("unknown mercato type %1").arg(strongAccentType), &_e);
-                              _e.readNext();
-                              }
-                        else
-                              skipLogCurrElem();
-                        }
-                  //qDebug("::notations tokenString '%s' name '%s'", qPrintable(_e.tokenString()), qPrintable(_e.name().toString()));
+            else if (_e.name() == "caesura") {
+                  breath = 3;
+                  _e.readNext();
                   }
-            else if (_e.name() == "fermata")
-                  fermata(cr);
-            else if (_e.name() == "ornaments") {
-                  bool trillMark = false;
-                  // <trill-mark placement="above"/>
-                  while (_e.readNextStartElement()) {
-                        if (addMxmlArticulationToChord(cr, _e.name().toString())) {
-                              _e.readNext();
-                              continue;
-                              }
-                        else if (_e.name() == "trill-mark") {
-                              trillMark = true;
-                              _e.readNext();
-                              }
-                        else if (_e.name() == "wavy-line") {
-                              wavyLineType = _e.attributes().value("type").toString();
-                              wavyLineNo   = _e.attributes().value("number").toString().toInt();
-                              if (wavyLineNo > 0) wavyLineNo--;
-                              // any grace note containing a wavy-line stop means
-                              // last note of a grace after set has been found
-                              if (wavyLineType == "stop" && cr->isGrace())
-                                    lastGraceAFter = true;
-                              _e.readNext();
-                              }
-                        else if (_e.name() == "tremolo") {
-                              tremoloType = _e.attributes().value("type").toString();
-                              tremolo = _e.readElementText().toInt();
-                              }
-                        else if (_e.name() == "accidental-mark")
-                              skipLogCurrElem();
-                        else if (_e.name() == "delayed-turn") {
-                              // TODO: actually this should be offset a bit to the right
-                              addArticulationToChord(cr, ArticulationType::Turn, "");
-                              _e.readNext();
-                              }
-                        else if (_e.name() == "inverted-mordent"
-                                 || _e.name() == "mordent") {
-                              addMordentToChord(cr, _e.name().toString(),
-                                                _e.attributes().value("long").toString(),
-                                                _e.attributes().value("approach").toString(),
-                                                _e.attributes().value("departure").toString());
-                              _e.readNext();
-                              }
-                        else
-                              skipLogCurrElem();
-                        }
-                  //qDebug("::notations tokenString '%s' name '%s'", qPrintable(_e.tokenString()), qPrintable(_e.name().toString()));
-                  // note that mscore wavy line already implicitly includes a trillsym
-                  // so don't add an additional one
-                  if (trillMark && wavyLineType != "start")
-                        addArticulationToChord(cr, ArticulationType::Trill, "");
+            else if (_e.name() == "doit"
+                     || _e.name() == "falloff"
+                     || _e.name() == "plop"
+                     || _e.name() == "scoop") {
+                  chordLineType = _e.name().toString();
+                  _e.readNext();
                   }
-            else if (_e.name() == "technical") {
-                  while (_e.readNextStartElement()) {
-                        if (addMxmlArticulationToChord(cr, _e.name().toString())) {
-                              _e.readNext();
-                              continue;
-                              }
-                        else if (_e.name() == "fingering")
-                              // TODO: distinguish between keyboards (style TextStyleType::FINGERING)
-                              // and (plucked) strings (style TextStyleType::LH_GUITAR_FINGERING)
-                              addTextToNote(_e.lineNumber(), _e.columnNumber(), _e.readElementText(),
-                                            TextStyleType::FINGERING, _score, note);
-                        else if (_e.name() == "fret") {
-                              int fret = _e.readElementText().toInt();
-                              if (note) {
-                                    if (note->staff()->isTabStaff())
-                                          note->setFret(fret);
-                                    }
-                              else
-                                    _logger->logError("no note for fret", &_e);
-                              }
-                        else if (_e.name() == "pluck")
-                              addTextToNote(_e.lineNumber(), _e.columnNumber(), _e.readElementText(),
-                                            TextStyleType::RH_GUITAR_FINGERING, _score, note);
-                        else if (_e.name() == "string") {
-                              QString txt = _e.readElementText();
-                              if (note) {
-                                    if (note->staff()->isTabStaff())
-                                          note->setString(txt.toInt() - 1);
-                                    else
-                                          addTextToNote(_e.lineNumber(), _e.columnNumber(), txt,
-                                                        TextStyleType::STRING_NUMBER, _score, note);
-                                    }
-                              else
-                                    _logger->logError("no note for string", &_e);
-                              }
-                        else if (_e.name() == "pull-off")
-                              skipLogCurrElem();
-                        else
-                              skipLogCurrElem();
-                        }
-                  //qDebug("::notations tokenString '%s' name '%s'", qPrintable(_e.tokenString()), qPrintable(_e.name().toString()));
+            else if (_e.name() == "strong-accent") {
+                  QString strongAccentType = _e.attributes().value("type").toString();
+                  if (strongAccentType == "up" || strongAccentType == "")
+                        addArticulationToChord(cr, ArticulationType::Marcato, "up");
+                  else if (strongAccentType == "down")
+                        addArticulationToChord(cr, ArticulationType::Marcato, "down");
+                  else
+                        _logger->logError(QString("unknown mercato type %1").arg(strongAccentType), &_e);
+                  _e.readNext();
                   }
-            else if (_e.name() == "arpeggiate") {
-                  arpeggioType = _e.attributes().value("direction").toString();
-                  if (arpeggioType == "") arpeggioType = "none";
+            else
+                  skipLogCurrElem();
+            }
+      //qDebug("::notations tokenString '%s' name '%s'", qPrintable(_e.tokenString()), qPrintable(_e.name().toString()));
+      }
+
+//---------------------------------------------------------
+//   ornaments
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/notations/ornaments node.
+ */
+
+void MusicXMLParserPass2::ornaments(ChordRest* cr,
+                                    QString& wavyLineType,
+                                    int& wavyLineNo,
+                                    QString& tremoloType,
+                                    int& tremoloNr, bool& lastGraceAFter)
+      {
+      Q_ASSERT(_e.isStartElement() && _e.name() == "ornaments");
+
+      bool trillMark = false;
+      // <trill-mark placement="above"/>
+      while (_e.readNextStartElement()) {
+            if (addMxmlArticulationToChord(cr, _e.name().toString())) {
+                  _e.readNext();
+                  continue;
+                  }
+            else if (_e.name() == "trill-mark") {
+                  trillMark = true;
                   _e.readNext();
                   }
-            else if (_e.name() == "non-arpeggiate") {
-                  arpeggioType = "non-arpeggiate";
+            else if (_e.name() == "wavy-line") {
+                  wavyLineType = _e.attributes().value("type").toString();
+                  wavyLineNo   = _e.attributes().value("number").toString().toInt();
+                  if (wavyLineNo > 0) wavyLineNo--;
+                  // any grace note containing a wavy-line stop means
+                  // last note of a grace after set has been found
+                  if (wavyLineType == "stop" && cr->isGrace())
+                        lastGraceAFter = true;
                   _e.readNext();
                   }
-            else if (_e.name() == "glissando" || _e.name() == "slide") {
-                  int n                   = _e.attributes().value("number").toString().toInt();
-                  if (n > 0) n--;
-                  QString spannerType     = _e.attributes().value("type").toString();
-                  int tag                 = _e.name() == "slide" ? 0 : 1;
-                  //                  QString lineType  = ee.attribute(QString("line-type"), "solid");
-                  Glissando*& gliss = _glissandi[n][tag];
-                  if (spannerType == "start") {
-                        QColor color(_e.attributes().value("color").toString());
-                        QString glissText = _e.readElementText();
-                        if (gliss) {
-                              _logger->logError(QString("overlapping glissando/slide number %1").arg(n+1), &_e);
-                              }
-                        else if (!note) {
-                              _logger->logError(QString("no note for glissando/slide number %1 start").arg(n+1), &_e);
-                              }
-                        else {
-                              gliss = new Glissando(_score);
-                              gliss->setAnchor(Spanner::Anchor::NOTE);
-                              gliss->setStartElement(note);
-                              gliss->setTick(tick);
-                              gliss->setTrack(track);
-                              gliss->setParent(note);
-                              if (color.isValid())
-                                    gliss->setColor(color);
-                              gliss->setText(glissText);
-                              gliss->setGlissandoType(tag == 0 ? Glissando::Type::STRAIGHT : Glissando::Type::WAVY);
-                              _spanners[gliss] = QPair<int, int>(tick, -1);
-                              // qDebug("glissando/slide=%p inserted at first tick %d", gliss, tick);
-                              }
+            else if (_e.name() == "tremolo") {
+                  tremoloType = _e.attributes().value("type").toString();
+                  tremoloNr = _e.readElementText().toInt();
+                  }
+            else if (_e.name() == "accidental-mark")
+                  skipLogCurrElem();
+            else if (_e.name() == "delayed-turn") {
+                  // TODO: actually this should be offset a bit to the right
+                  addArticulationToChord(cr, ArticulationType::Turn, "");
+                  _e.readNext();
+                  }
+            else if (_e.name() == "inverted-mordent"
+                     || _e.name() == "mordent") {
+                  addMordentToChord(cr, _e.name().toString(),
+                                    _e.attributes().value("long").toString(),
+                                    _e.attributes().value("approach").toString(),
+                                    _e.attributes().value("departure").toString());
+                  _e.readNext();
+                  }
+            else
+                  skipLogCurrElem();
+            }
+      //qDebug("::notations tokenString '%s' name '%s'", qPrintable(_e.tokenString()), qPrintable(_e.name().toString()));
+      // note that mscore wavy line already implicitly includes a trillsym
+      // so don't add an additional one
+      if (trillMark && wavyLineType != "start")
+            addArticulationToChord(cr, ArticulationType::Trill, "");
+      }
+
+//---------------------------------------------------------
+//   technical
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/notations/technical node.
+ */
+
+void MusicXMLParserPass2::technical(Note* note, ChordRest* cr)
+      {
+      Q_ASSERT(_e.isStartElement() && _e.name() == "technical");
+
+      while (_e.readNextStartElement()) {
+            if (addMxmlArticulationToChord(cr, _e.name().toString())) {
+                  _e.readNext();
+                  continue;
+                  }
+            else if (_e.name() == "fingering")
+                  // TODO: distinguish between keyboards (style TextStyleType::FINGERING)
+                  // and (plucked) strings (style TextStyleType::LH_GUITAR_FINGERING)
+                  addTextToNote(_e.lineNumber(), _e.columnNumber(), _e.readElementText(),
+                                TextStyleType::FINGERING, _score, note);
+            else if (_e.name() == "fret") {
+                  int fret = _e.readElementText().toInt();
+                  if (note) {
+                        if (note->staff()->isTabStaff())
+                              note->setFret(fret);
                         }
-                  else if (spannerType == "stop") {
-                        if (!gliss) {
-                              _logger->logError(QString("glissando/slide number %1 stop without start").arg(n+1), &_e);
-                              }
-                        else if (!note) {
-                              _logger->logError(QString("no note for glissando/slide number %1 stop").arg(n+1), &_e);
-                              }
-                        else {
-                              _spanners[gliss].second = tick + ticks;
-                              gliss->setEndElement(note);
-                              gliss->setTick2(tick);
-                              gliss->setTrack2(track);
-                              // qDebug("glissando/slide=%p second tick %d", gliss, tick);
-                              gliss = 0;
-                              }
+                  else
+                        _logger->logError("no note for fret", &_e);
+                  }
+            else if (_e.name() == "pluck")
+                  addTextToNote(_e.lineNumber(), _e.columnNumber(), _e.readElementText(),
+                                TextStyleType::RH_GUITAR_FINGERING, _score, note);
+            else if (_e.name() == "string") {
+                  QString txt = _e.readElementText();
+                  if (note) {
+                        if (note->staff()->isTabStaff())
+                              note->setString(txt.toInt() - 1);
+                        else
+                              addTextToNote(_e.lineNumber(), _e.columnNumber(), txt,
+                                            TextStyleType::STRING_NUMBER, _score, note);
                         }
                   else
-                        _logger->logError(QString("unknown glissando/slide type %1").arg(spannerType), &_e);
-                  _e.readNext();
+                        _logger->logError("no note for string", &_e);
                   }
+            else if (_e.name() == "pull-off")
+                  skipLogCurrElem();
             else
                   skipLogCurrElem();
             }
+      //qDebug("::notations tokenString '%s' name '%s'", qPrintable(_e.tokenString()), qPrintable(_e.name().toString()));
+      }
+
+//---------------------------------------------------------
+//   glissando
+//---------------------------------------------------------
+
+/**
+ Parse the /score-partwise/part/measure/note/notations/glissando
+ and /score-partwise/part/measure/note/notations/slide nodes.
+ */
 
+void MusicXMLParserPass2::glissando(Note* note, const int tick, const int ticks, const int track)
+      {
+      Q_ASSERT(_e.isStartElement() && (_e.name() == "glissando" || _e.name() == "slide"));
+
+      int n                   = _e.attributes().value("number").toString().toInt();
+      if (n > 0) n--;
+      QString spannerType     = _e.attributes().value("type").toString();
+      int tag                 = _e.name() == "slide" ? 0 : 1;
+      //                  QString lineType  = ee.attribute(QString("line-type"), "solid");
+      Glissando*& gliss = _glissandi[n][tag];
+      if (spannerType == "start") {
+            QColor color(_e.attributes().value("color").toString());
+            QString glissText = _e.readElementText();
+            if (gliss) {
+                  _logger->logError(QString("overlapping glissando/slide number %1").arg(n+1), &_e);
+                  }
+            else if (!note) {
+                  _logger->logError(QString("no note for glissando/slide number %1 start").arg(n+1), &_e);
+                  }
+            else {
+                  gliss = new Glissando(_score);
+                  gliss->setAnchor(Spanner::Anchor::NOTE);
+                  gliss->setStartElement(note);
+                  gliss->setTick(tick);
+                  gliss->setTrack(track);
+                  gliss->setParent(note);
+                  if (color.isValid())
+                        gliss->setColor(color);
+                  gliss->setText(glissText);
+                  gliss->setGlissandoType(tag == 0 ? Glissando::Type::STRAIGHT : Glissando::Type::WAVY);
+                  _spanners[gliss] = QPair<int, int>(tick, -1);
+                  // qDebug("glissando/slide=%p inserted at first tick %d", gliss, tick);
+                  }
+            }
+      else if (spannerType == "stop") {
+            if (!gliss) {
+                  _logger->logError(QString("glissando/slide number %1 stop without start").arg(n+1), &_e);
+                  }
+            else if (!note) {
+                  _logger->logError(QString("no note for glissando/slide number %1 stop").arg(n+1), &_e);
+                  }
+            else {
+                  _spanners[gliss].second = tick + ticks;
+                  gliss->setEndElement(note);
+                  gliss->setTick2(tick);
+                  gliss->setTrack2(track);
+                  // qDebug("glissando/slide=%p second tick %d", gliss, tick);
+                  gliss = 0;
+                  }
+            }
+      else
+            _logger->logError(QString("unknown glissando/slide type %1").arg(spannerType), &_e);
+      _e.readNext();
+      }
+
+//---------------------------------------------------------
+//   addArpeggio
+//---------------------------------------------------------
+
+static void addArpeggio(ChordRest* cr, const QString& arpeggioType,
+                        MxmlLogger* logger, const QXmlStreamReader* const xmlreader)
+      {
       // no support for arpeggio on rest
       if (!arpeggioType.isEmpty() && cr->type() == Element::Type::CHORD) {
-            Arpeggio* a = new Arpeggio(_score);
+            Arpeggio* a = new Arpeggio(cr->score());
             if (arpeggioType == "none")
                   a->setArpeggioType(ArpeggioType::NORMAL);
             else if (arpeggioType == "up")
@@ -5923,7 +5606,7 @@ void MusicXMLParserPass2::notations(Note* note, ChordRest* cr, const int tick,
             else if (arpeggioType == "non-arpeggiate")
                   a->setArpeggioType(ArpeggioType::BRACKET);
             else {
-                  _logger->logError(QString("unknown arpeggio type %1").arg(arpeggioType), &_e);
+                  logger->logError(QString("unknown arpeggio type %1").arg(arpeggioType), xmlreader);
                   delete a;
                   a = 0;
                   }
@@ -5935,49 +5618,23 @@ void MusicXMLParserPass2::notations(Note* note, ChordRest* cr, const int tick,
             else
                   cr->add(a);
             }
+      }
 
-      if (!wavyLineType.isEmpty()) {
-            Trill*& t = _trills[wavyLineNo];
-            if (wavyLineType == "start") {
-                  if (t) {
-                        _logger->logError(QString("overlapping wavy-line number %1").arg(wavyLineNo+1), &_e);
-                        }
-                  else {
-                        t = new Trill(_score);
-                        t->setTrack(trk);
-                        _spanners[t] = QPair<int, int>(tick, -1);
-                        // qDebug("wedge trill=%p inserted at first tick %d", trill, tick);
-                        }
-                  }
-            else if (wavyLineType == "stop") {
-                  if (!t) {
-                        _logger->logError(QString("wavy-line number %1 stop without start").arg(wavyLineNo+1), &_e);
-                        }
-                  else {
-                        _spanners[t].second = tick + ticks;
-                        // qDebug("wedge trill=%p second tick %d", trill, tick);
-                        t = 0;
-                        }
-                  }
-            else
-                  _logger->logError(QString("unknown wavy-line type %1").arg(wavyLineType), &_e);
-            }
-
-      if (breath >= 0 && !cr->isGrace()) {
-            Breath* b = new Breath(_score);
-            // b->setTrack(trk + voice); TODO check next line
-            b->setTrack(track);
-            b->setBreathType(breath);
-            Segment* seg = measure->getSegment(Segment::Type::Breath, tick + ticks);
-            seg->add(b);
-            }
+//---------------------------------------------------------
+//   addTremolo
+//---------------------------------------------------------
 
-      if (tremolo) {
-            //qDebug("tremolo %d type '%s' ticks %d tremStart %p", tremolo, qPrintable(tremoloType), ticks, _tremStart);
-            if (tremolo == 1 || tremolo == 2 || tremolo == 3 || tremolo == 4) {
+static void addTremolo(ChordRest* cr,
+                       const int tremoloNr, const QString& tremoloType, const int ticks,
+                       Chord*& tremStart,
+                       MxmlLogger* logger, const QXmlStreamReader* const xmlreader)
+      {
+      if (tremoloNr) {
+            //qDebug("tremolo %d type '%s' ticks %d tremStart %p", tremoloNr, qPrintable(tremoloType), ticks, _tremStart);
+            if (tremoloNr == 1 || tremoloNr == 2 || tremoloNr == 3 || tremoloNr == 4) {
                   if (tremoloType == "" || tremoloType == "single") {
-                        Tremolo* t = new Tremolo(_score);
-                        switch (tremolo) {
+                        Tremolo* t = new Tremolo(cr->score());
+                        switch (tremoloNr) {
                               case 1: t->setTremoloType(TremoloType::R8); break;
                               case 2: t->setTremoloType(TremoloType::R16); break;
                               case 3: t->setTremoloType(TremoloType::R32); break;
@@ -5986,19 +5643,19 @@ void MusicXMLParserPass2::notations(Note* note, ChordRest* cr, const int tick,
                         cr->add(t);
                         }
                   else if (tremoloType == "start") {
-                        if (_tremStart) _logger->logError("MusicXML::import: double tremolo start", &_e);
-                        _tremStart = static_cast<Chord*>(cr);
+                        if (tremStart) logger->logError("MusicXML::import: double tremolo start", xmlreader);
+                        tremStart = static_cast<Chord*>(cr);
                         }
                   else if (tremoloType == "stop") {
-                        if (_tremStart) {
-                              Tremolo* t = new Tremolo(_score);
-                              switch (tremolo) {
+                        if (tremStart) {
+                              Tremolo* t = new Tremolo(cr->score());
+                              switch (tremoloNr) {
                                     case 1: t->setTremoloType(TremoloType::C8); break;
                                     case 2: t->setTremoloType(TremoloType::C16); break;
                                     case 3: t->setTremoloType(TremoloType::C32); break;
                                     case 4: t->setTremoloType(TremoloType::C64); break;
                                     }
-                              t->setChords(_tremStart, static_cast<Chord*>(cr));
+                              t->setChords(tremStart, static_cast<Chord*>(cr));
                               // fixup chord duration and type
                               const int tremDur = ticks / 2;
                               t->chord1()->setDurationType(tremDur);
@@ -6006,19 +5663,84 @@ void MusicXMLParserPass2::notations(Note* note, ChordRest* cr, const int tick,
                               t->chord2()->setDurationType(tremDur);
                               t->chord2()->setDuration(Fraction::fromTicks(tremDur));
                               // add tremolo to first chord (only)
-                              _tremStart->add(t);
+                              tremStart->add(t);
                               }
-                        else _logger->logError("MusicXML::import: double tremolo stop w/o start", &_e);
-                        _tremStart = 0;
+                        else logger->logError("MusicXML::import: double tremolo stop w/o start", xmlreader);
+                        tremStart = 0;
                         }
                   }
             else
-                  _logger->logError(QString("unknown tremolo type %1").arg(tremolo), &_e);
+                  logger->logError(QString("unknown tremolo type %1").arg(tremoloNr), xmlreader);
             }
+      }
+
+//---------------------------------------------------------
+//   addWavyLine
+//---------------------------------------------------------
+
+static void addWavyLine(ChordRest* cr, const int tick,
+                        const int wavyLineNo, const QString& wavyLineType,
+                        MusicXmlSpannerMap& spanners, TrillStack& trills,
+                        MxmlLogger* logger, const QXmlStreamReader* const xmlreader)
+      {
+      if (!wavyLineType.isEmpty()) {
+            const auto ticks = cr->duration().ticks();
+            const auto track = cr->track();
+            const auto trk = (track / VOICES) * VOICES;             // first track of staff
+            Trill*& t = trills[wavyLineNo];
+            if (wavyLineType == "start") {
+                  if (t) {
+                        logger->logError(QString("overlapping wavy-line number %1").arg(wavyLineNo+1), xmlreader);
+                        }
+                  else {
+                        t = new Trill(cr->score());
+                        t->setTrack(trk);
+                        spanners[t] = QPair<int, int>(tick, -1);
+                        // qDebug("wedge trill=%p inserted at first tick %d", trill, tick);
+                        }
+                  }
+            else if (wavyLineType == "stop") {
+                  if (!t) {
+                        logger->logError(QString("wavy-line number %1 stop without start").arg(wavyLineNo+1), xmlreader);
+                        }
+                  else {
+                        spanners[t].second = tick + ticks;
+                        // qDebug("wedge trill=%p second tick %d", trill, tick);
+                        t = 0;
+                        }
+                  }
+            else
+                  logger->logError(QString("unknown wavy-line type %1").arg(wavyLineType), xmlreader);
+            }
+      }
+
+//---------------------------------------------------------
+//   addBreath
+//---------------------------------------------------------
+
+static void addBreath(ChordRest* cr, const int tick, const int breath)
+      {
+      if (breath >= 0 && !cr->isGrace()) {
+            Breath* b = new Breath(cr->score());
+            // b->setTrack(trk + voice); TODO check next line
+            b->setTrack(cr->track());
+            b->setBreathType(breath);
+            const auto ticks = cr->duration().ticks();
+            auto seg = cr->measure()->getSegment(Segment::Type::Breath, tick + ticks);
+            seg->add(b);
+            }
+      }
+
+//---------------------------------------------------------
+//   addChordLine
+//---------------------------------------------------------
 
+static void addChordLine(Note* note, const QString& chordLineType,
+                         MxmlLogger* logger, const QXmlStreamReader* const xmlreader)
+      {
       if (chordLineType != "") {
             if (note) {
-                  ChordLine* cl = new ChordLine(_score);
+                  ChordLine* cl = new ChordLine(note->score());
                   if (chordLineType == "falloff")
                         cl->setChordLineType(ChordLineType::FALL);
                   if (chordLineType == "doit")
@@ -6030,13 +5752,106 @@ void MusicXMLParserPass2::notations(Note* note, ChordRest* cr, const int tick,
                   note->chord()->add(cl);
                   }
             else
-                  _logger->logError(QString("no note for %1").arg(chordLineType), &_e);
+                  logger->logError(QString("no note for %1").arg(chordLineType), xmlreader);
+            }
+      }
+
+//---------------------------------------------------------
+//   notations
+//---------------------------------------------------------
+
+
+/**
+ Parse the /score-partwise/part/measure/note/notations node.
+ Note that some notations attach to notes only in MuseScore,
+ which means trying to attach them to a rest will crash,
+ as in that case note is 0.
+ */
+
+void MusicXMLParserPass2::notations(Note* note, ChordRest* cr, const int tick,
+                                    MusicXmlTupletDesc& tupletDesc, bool& lastGraceAFter)
+      {
+      Q_ASSERT(_e.isStartElement() && _e.name() == "notations");
+
+      lastGraceAFter = false;       // ensure default
+
+      Measure* measure = cr->measure();
+      int ticks = cr->duration().ticks();
+      int track = cr->track();
+
+      QString wavyLineType;
+      int wavyLineNo = 0;
+      QString arpeggioType;
+      //      QString glissandoType;
+      int breath = -1;
+      int tremoloNr = 0;
+      QString tremoloType;
+      QString placement;
+      QStringList dynamicslist;
+      // qreal rx = 0.0;
+      // qreal ry = 0.0;
+      qreal yoffset = 0.0; // actually this is default-y
+      // qreal xoffset = 0.0; // not used
+      bool hasYoffset = false;
+      QString chordLineType;
+
+      while (_e.readNextStartElement()) {
+            if (_e.name() == "slur") {
+                  slur(cr, tick, track, lastGraceAFter);
+                  }
+            else if (_e.name() == "tied") {
+                  tied(note, track);
+                  }
+            else if (_e.name() == "tuplet") {
+                  tuplet(tupletDesc);
+                  }
+            else if (_e.name() == "dynamics") {
+                  placement = _e.attributes().value("placement").toString();
+                  if (preferences.musicxmlImportLayout) {
+                        // ry        = ee.attribute(QString("relative-y"), "0").toDouble() * -.1;
+                        // rx        = ee.attribute(QString("relative-x"), "0").toDouble() * .1;
+                        yoffset   = _e.attributes().value("default-y").toDouble(&hasYoffset) * -0.1;
+                        // xoffset   = ee.attribute("default-x", "0.0").toDouble() * 0.1;
+                        }
+                  dynamics(placement, dynamicslist);
+                  }
+            else if (_e.name() == "articulations") {
+                  articulations(cr, breath, chordLineType);
+                  }
+            else if (_e.name() == "fermata")
+                  fermata(cr);
+            else if (_e.name() == "ornaments") {
+                  ornaments(cr, wavyLineType, wavyLineNo, tremoloType, tremoloNr, lastGraceAFter);
+                  }
+            else if (_e.name() == "technical") {
+                  technical(note, cr);
+                  }
+            else if (_e.name() == "arpeggiate") {
+                  arpeggioType = _e.attributes().value("direction").toString();
+                  if (arpeggioType == "") arpeggioType = "none";
+                  _e.readNext();
+                  }
+            else if (_e.name() == "non-arpeggiate") {
+                  arpeggioType = "non-arpeggiate";
+                  _e.readNext();
+                  }
+            else if (_e.name() == "glissando" || _e.name() == "slide") {
+                  glissando(note, tick, ticks, track);
+                  }
+            else
+                  skipLogCurrElem();
             }
 
+      addArpeggio(cr, arpeggioType, _logger, &_e);
+      addWavyLine(cr, tick, wavyLineNo, wavyLineType, _spanners, _trills, _logger, &_e);
+      addBreath(cr, tick, breath);
+      addTremolo(cr, tremoloNr, tremoloType, ticks, _tremStart, _logger, &_e);
+      addChordLine(note, chordLineType, _logger, &_e);
+
       // more than one dynamic ???
       // LVIFIX: check import/export of <other-dynamics>unknown_text</...>
       // TODO remove duplicate code (see MusicXml::direction)
-      for (QStringList::Iterator it = dynamics.begin(); it != dynamics.end(); ++it ) {
+      for (QStringList::Iterator it = dynamicslist.begin(); it != dynamicslist.end(); ++it ) {
             Dynamic* dyn = new Dynamic(_score);
             dyn->setDynamicType(*it);
             if (hasYoffset) dyn->textStyle().setYoff(yoffset);
index 4b7e8b3..72fcec0 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef __IMPORTMXMLPASS2_H__
 #define __IMPORTMXMLPASS2_H__
 
+#include <array>
+
 #include "libmscore/score.h"
 #include "libmscore/tuplet.h"
 #include "importxmlfirstpass.h"
@@ -99,6 +101,13 @@ class Pedal;
 class Trill;
 class MxmlLogger;
 
+using SlurStack = std::array<SlurDesc, MAX_NUMBER_LEVEL>;
+using TrillStack = std::array<Trill*, MAX_NUMBER_LEVEL>;
+using BracketsStack = std::array<SLine*, MAX_BRACKETS>;
+using DashesStack = std::array<SLine*, MAX_DASHES>;
+using OttavasStack = std::array<SLine*, MAX_NUMBER_LEVEL>;
+using HairpinsStack = std::array<SLine*, MAX_NUMBER_LEVEL>;
+
 class MusicXMLParserPass2 {
 public:
       MusicXMLParserPass2(Score* score, MusicXMLParserPass1& pass1, MxmlLogger* logger);
@@ -135,10 +144,16 @@ public:
       void forward(Fraction& dura);
       void backup(Fraction& dura);
       void timeModification(Fraction& timeMod, TDuration& normalType);
-      void pitch(int& step, int& alter, int& oct, AccidentalType& accid);
-      void rest(int& step, int& octave);
+      //void pitch(int& step, int& alter, int& oct, AccidentalType& accid);
       void lyric(QMap<int, Lyrics*>& numbrdLyrics, QMap<int, Lyrics*>& defyLyrics,
                  QList<Lyrics*>& unNumbrdLyrics, QSet<Lyrics*>& extLyrics);
+      void slur(ChordRest* cr, const int tick, const int track, bool& lastGraceAFter);
+      void tied(Note* note, const int track);
+      void articulations(ChordRest* cr, int& breath, QString& chordLineType);
+      void dynamics(QString& placement, QStringList& dynamics);
+      void ornaments(ChordRest* cr, QString& wavyLineType, int& wavyLineNo, QString& tremoloType, int& tremoloNr, bool& lastGraceAFter);
+      void technical(Note* note, ChordRest* cr);
+      void glissando(Note* note, const int tick, const int ticks, const int track);
       void notations(Note* note, ChordRest* cr, const int tick, MusicXmlTupletDesc& tupletDesc, bool& lastGraceAFter);
       void stem(MScore::Direction& sd, bool& nost);
       void fermata(ChordRest* cr);
@@ -173,15 +188,14 @@ private:
       // or use score->sigmap() ?
       Fraction _timeSigDura;
 
-      QVector<Tuplet*> _tuplets;                 ///< Current tuplet for each track in the current part
-      QVector<bool> _tuplImpls;                 ///< Current tuplet implicit flag for each track in the current part
-      SlurDesc _slur[MAX_NUMBER_LEVEL];
-      Trill* _trills[MAX_NUMBER_LEVEL];          ///< Current trills
-      SLine* _brackets[MAX_BRACKETS];
-      SLine* _dashes[MAX_DASHES];
-      SLine* _ottavas[MAX_NUMBER_LEVEL];        ///< Current ottavas
-      SLine* _hairpins[MAX_NUMBER_LEVEL];      ///< Current hairpins
-      // TODO SLine* trills[MAX_NUMBER_LEVEL];          ///< Current trills
+      QVector<Tuplet*> _tuplets;          ///< Current tuplet for each track in the current part
+      QVector<bool> _tuplImpls;           ///< Current tuplet implicit flag for each track in the current part
+      SlurStack _slurs {{}};
+      TrillStack _trills {{}};            ///< Current trills
+      BracketsStack _brackets {{}};
+      DashesStack _dashes {{}};
+      OttavasStack _ottavas {{}};         ///< Current ottavas
+      HairpinsStack _hairpins {{}};       ///< Current hairpins
 
       Glissando* _glissandi[MAX_NUMBER_LEVEL][2];   ///< Current slides ([0]) / glissandi ([1])
 
index 8beb92f..b04d220 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #include "icons.h"
 #include "instrwidget.h"
+#include "stringutils.h"
 
 #include "libmscore/clef.h"
 #include "libmscore/instrtemplate.h"
@@ -56,7 +57,12 @@ void filterInstruments(QTreeWidget* instrumentList, const QString &searchPhrase)
             for (int cidx = 0; (ci = item->child(cidx)); ++cidx) {
                   // replace the unicode b (accidential) so a search phrase of "bb" would give Bb Trumpet...
                   QString text = ci->text(0).replace(QChar(0x266d), QChar('b'));
-                  bool isMatch = text.contains(searchPhrase, Qt::CaseInsensitive);
+
+                  // remove ligatures and diacritics
+                  QString removedSpecialChar = stringutils::removeLigatures(text);
+                  removedSpecialChar = stringutils::removeDiacritics(removedSpecialChar);
+
+                  bool isMatch = text.contains(searchPhrase, Qt::CaseInsensitive) || removedSpecialChar.contains(searchPhrase, Qt::CaseInsensitive);
                   ci->setHidden(!isMatch);
 
                   if (isMatch)
diff --git a/mscore/macos/cocoabridge.h b/mscore/macos/cocoabridge.h
new file mode 100644 (file)
index 0000000..f8e82e1
--- /dev/null
@@ -0,0 +1,31 @@
+//=============================================================================
+//  MuseScore
+//  Music Composition & Notation
+//
+//  Copyright (C) 2008-2018 Werner Schweer and others
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License version 2.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//=============================================================================
+
+#ifndef __COCOABRIDGE_H__
+#define __COCOABRIDGE_H__
+
+class CocoaBridge
+{
+      CocoaBridge() {}
+
+   public:
+      static void setAllowsAutomaticWindowTabbing(bool flag);
+};
+
+#endif
diff --git a/mscore/macos/cocoabridge.mm b/mscore/macos/cocoabridge.mm
new file mode 100644 (file)
index 0000000..d49fa69
--- /dev/null
@@ -0,0 +1,27 @@
+//=============================================================================
+//  MuseScore
+//  Music Composition & Notation
+//
+//  Copyright (C) 2008-2018 Werner Schweer and others
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License version 2.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//=============================================================================
+
+#include "cocoabridge.h"
+#import <Cocoa/Cocoa.h>
+
+void  CocoaBridge::setAllowsAutomaticWindowTabbing(bool flag)
+{
+      if ([NSWindow respondsToSelector:@selector(allowsAutomaticWindowTabbing)])
+            [NSWindow setAllowsAutomaticWindowTabbing: flag];
+}
index 22981a6..639b836 100644 (file)
               <verstretch>0</verstretch>
              </sizepolicy>
             </property>
+            <property name="toolTip">
+             <string>Layout stretch factor</string>
+            </property>
             <property name="text">
              <string>Layout stretch:</string>
             </property>
index d147e3b..b79ef4e 100644 (file)
 #include "exportmp3.h"
 #endif
 
+#ifdef Q_OS_MAC
+#include "macos/cocoabridge.h"
+#endif
+
 #ifdef AEOLUS
 extern Ms::Synthesizer* createAeolus();
 #endif
@@ -1278,6 +1282,8 @@ void MuseScore::selectionChanged(SelState selectionState)
             pianorollEditor->changeSelection(selectionState);
       if (drumrollEditor)
             drumrollEditor->changeSelection(selectionState);
+      if (_pianoTools && _pianoTools->isVisible())
+            _pianoTools->changeSelection(cs->selection());
       if (_inspector)
             updateInspector();
       }
@@ -5853,7 +5859,7 @@ int main(int argc, char* av[])
                   if (!name.isEmpty())
                         ++files;
                   }
-#ifdef Q_WS_MAC
+#ifdef Q_OS_MAC
             // app->paths contains files requested to be loaded by OS X
             // append these to argv and update file count
             foreach(const QString& name, app->paths) {
@@ -5878,6 +5884,9 @@ int main(int argc, char* av[])
       // there's a bug in Qt showing the toolbar unified after switching showFullScreen(), showMaximized(),
       // showNormal()...
       mscore->setUnifiedTitleAndToolBarOnMac(false);
+
+      // don't let macOS add "Show Tab Bar" to "View" Menu
+      CocoaBridge::setAllowsAutomaticWindowTabbing(false);
 #endif
 
       mscore->changeState(mscore->noScore() ? STATE_DISABLED : STATE_NORMAL);
index 7f23906..49da817 100644 (file)
@@ -161,7 +161,7 @@ void ValidatorMessageHandler::handleMessage(QtMsgType type, const QString& descr
       if (!desc.setContent(description, false, &contentError, &contentLine,
                            &contentColumn)) {
             qDebug("ValidatorMessageHandler: could not parse validation error line %d column %d: %s",
-               contentLine, contentColumn, qPrintable(contentError));
+                   contentLine, contentColumn, qPrintable(contentError));
             return;
             }
       QDomElement e = desc.documentElement();
@@ -387,7 +387,7 @@ QString accSymId2MxmlString(const SymId id)
             case SymId::accidentalBuyukMucennebSharp:    s = "slash-sharp";          break;
             case SymId::accidentalBakiyeFlat:            s = "slash-flat";           break;
             case SymId::accidentalBuyukMucennebFlat:     s = "double-slash-flat";    break;
-                  /* TODO
+            /* TODO
             case AccidentalType::FLAT_ARROW_UP:      s = "flat-up";              break;
             case AccidentalType::NATURAL_ARROW_DOWN: s = "natural-down";         break;
             case AccidentalType::SHARP_ARROW_DOWN:   s = "sharp-down";           break;
@@ -399,7 +399,7 @@ QString accSymId2MxmlString(const SymId id)
             case AccidentalType::SHARP_ARROW_UP:     s = "sharp-up";             break;
             case AccidentalType::SORI:               s = "sori";                 break;
             case AccidentalType::KORON:              s = "koron";                break;
-                   */
+             */
             default:
                   qDebug("accSymId2MxmlString: unknown accidental %d", static_cast<int>(id));
             }
@@ -540,4 +540,55 @@ AccidentalType mxmlString2accidentalType(const QString mxmlName)
       return AccidentalType::NONE;
       }
 
+//---------------------------------------------------------
+//   isAppr
+//---------------------------------------------------------
+
+/**
+ Check if v approximately equals ref.
+ Used to prevent floating point comparison for equality from failing
+ */
+
+static bool isAppr(const double v, const double ref, const double epsilon)
+      {
+      return v > ref - epsilon && v < ref + epsilon;
+      }
+
+//---------------------------------------------------------
+//   microtonalGuess
+//---------------------------------------------------------
+
+/**
+ Convert a MusicXML alter tag into a microtonal accidental in MuseScore enum AccidentalType.
+ Works only for quarter tone, half tone, three-quarters tone and whole tone accidentals.
+ */
+
+AccidentalType microtonalGuess(double val)
+      {
+      const double eps = 0.001;
+      if (isAppr(val, -2, eps))
+            return AccidentalType::FLAT2;
+      else if (isAppr(val, -1.5, eps))
+            return AccidentalType::MIRRORED_FLAT2;
+      else if (isAppr(val, -1, eps))
+            return AccidentalType::FLAT;
+      else if (isAppr(val, -0.5, eps))
+            return AccidentalType::MIRRORED_FLAT;
+      else if (isAppr(val, 0, eps))
+            return AccidentalType::NATURAL;
+      else if (isAppr(val, 0.5, eps))
+            return AccidentalType::SHARP_SLASH;
+      else if (isAppr(val, 1, eps))
+            return AccidentalType::SHARP;
+      else if (isAppr(val, 1.5, eps))
+            return AccidentalType::SHARP_SLASH4;
+      else if (isAppr(val, 2, eps))
+            return AccidentalType::SHARP2;
+      else
+            qDebug("Guess for microtonal accidental corresponding to value %f failed.", val);              // TODO
+
+      // default
+      return AccidentalType::NONE;
+      }
+
 }
index e7c2205..8f2c7ca 100644 (file)
@@ -201,6 +201,7 @@ extern QString accSymId2MxmlString(const SymId id);
 extern QString accidentalType2MxmlString(const AccidentalType type);
 extern AccidentalType mxmlString2accidentalType(const QString mxmlName);
 extern SymId mxmlString2accSymId(const QString mxmlName);
+extern AccidentalType microtonalGuess(double val);
 
 } // namespace Ms
 #endif
diff --git a/mscore/paintengine_p.h b/mscore/paintengine_p.h
deleted file mode 100644 (file)
index ef50c09..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PAINTENGINE_P_H
-#define PAINTENGINE_P_H
-
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists for the convenience
-// of other Qt classes.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-QT_BEGIN_NAMESPACE
-
-class QPaintDevice;
-
-class QPaintEnginePrivate
-{
-    Q_DECLARE_PUBLIC(QPaintEngine)
-public:
-    QPaintEnginePrivate() : pdev(0), q_ptr(0), currentClipWidget(0), hasSystemTransform(0),
-                            hasSystemViewport(0) {}
-    virtual ~QPaintEnginePrivate() { }
-    QPaintDevice *pdev;
-    QPaintEngine *q_ptr;
-    QRegion systemClip;
-    QRect systemRect;
-    QRegion systemViewport;
-    QTransform systemTransform;
-    QWidget *currentClipWidget;
-    uint hasSystemTransform : 1;
-    uint hasSystemViewport : 1;
-
-    inline void transformSystemClip()
-    {
-        if (systemClip.isEmpty())
-            return;
-
-        if (hasSystemTransform) {
-            if (systemTransform.type() <= QTransform::TxTranslate)
-                systemClip.translate(qRound(systemTransform.dx()), qRound(systemTransform.dy()));
-            else
-                systemClip = systemTransform.map(systemClip);
-        }
-
-        // Make sure we're inside the viewport.
-        if (hasSystemViewport) {
-            systemClip &= systemViewport;
-            if (systemClip.isEmpty()) {
-                // We don't want to paint without system clip, so set it to 1 pixel :)
-                systemClip = QRect(systemViewport.boundingRect().topLeft(), QSize(1, 1));
-            }
-        }
-    }
-
-    inline void setSystemTransform(const QTransform &xform)
-    {
-        systemTransform = xform;
-        if ((hasSystemTransform = !xform.isIdentity()) || hasSystemViewport)
-            transformSystemClip();
-        systemStateChanged();
-    }
-
-    inline void setSystemViewport(const QRegion &region)
-    {
-        systemViewport = region;
-        hasSystemViewport = !systemViewport.isEmpty();
-    }
-
-    virtual void systemStateChanged() { }
-
-    void drawBoxTextItem(const QPointF &p, const QTextItemInt &ti);
-};
-
-QT_END_NAMESPACE
-
-#endif // QPAINTENGINE_P_H
index 8357fbf..c89e7fa 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "pianotools.h"
 #include "preferences.h"
+#include "libmscore/chord.h"
 
 namespace Ms {
 
@@ -168,6 +169,25 @@ void HPiano::releasePitch(int pitch)
       }
 
 //---------------------------------------------------------
+//   changeSelection
+//---------------------------------------------------------
+
+void HPiano::changeSelection(Selection selection)
+      {
+      for (PianoKeyItem* key : keys) {
+            key->setHighlighted(false);
+            key->setSelected(false);
+            }
+      for (Note* n : selection.uniqueNotes()) {
+            keys[n->pitch() - _firstKey]->setSelected(true);
+            for (Note* other : n->chord()->notes())
+                  keys[other->pitch() - _firstKey]->setHighlighted(true);
+            }
+      for (PianoKeyItem* key : keys)
+            key->update();
+      }
+
+//---------------------------------------------------------
 //   updateAllKeys
 //---------------------------------------------------------
 
@@ -189,6 +209,8 @@ PianoKeyItem::PianoKeyItem(HPiano* _piano, int p)
       piano = _piano;
       _pitch = p;
       _pressed = false;
+      _selected = false;
+      _highlighted = false;
       type = -1;
       }
 
@@ -329,6 +351,13 @@ void PianoKeyItem::paint(QPainter* p, const QStyleOptionGraphicsItem* /*o*/, QWi
             c.setAlpha(180);
             p->setBrush(c);
             }
+      else if (_selected) {
+            QColor c(preferences.pianoHlColor);
+            c.setAlpha(100);
+            p->setBrush(c);
+            }
+      else if (_highlighted)
+            p->setBrush(type >= 7 ? QColor(125, 125, 125) : QColor(200, 200, 200));
       else
             p->setBrush(type >= 7 ? Qt::black : Qt::white);
       p->drawPath(path());
@@ -437,5 +466,14 @@ bool HPiano::gestureEvent(QGestureEvent *event)
             }
       return true;
       }
+
+//---------------------------------------------------------
+//   changeSelection
+//---------------------------------------------------------
+
+void PianoTools::changeSelection(Selection selection)
+      {
+      _piano->changeSelection(selection);
+      }
 }
 
index e97f2b8..192ad1d 100644 (file)
@@ -22,6 +22,7 @@
 #define __PIANOTOOLS_H__
 
 #include "libmscore/note.h"
+#include "libmscore/select.h"
 
 namespace Ms {
 
@@ -35,6 +36,8 @@ class PianoKeyItem : public QGraphicsPathItem {
       int type;
       int _pitch;
       bool _pressed;
+      bool _highlighted;
+      bool _selected;
       HPiano* piano;
 
       virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
@@ -46,6 +49,8 @@ class PianoKeyItem : public QGraphicsPathItem {
       void setType(int val);
       int pitch() { return _pitch; }
       void setPressed(bool p) { _pressed = p; }
+      void setHighlighted(bool h) { _highlighted = h; }
+      void setSelected(bool s) { _selected = s; }
       };
 
 //---------------------------------------------------------
@@ -74,6 +79,7 @@ class HPiano : public QGraphicsView {
       void setPressedPitches(QSet<int> pitches);
       void pressPitch(int pitch);
       void releasePitch(int pitch);
+      void changeSelection(Selection selection);
       void updateAllKeys();
       virtual QSize sizeHint() const;
       };
@@ -96,6 +102,7 @@ class PianoTools : public QDockWidget {
       void pressPitch(int pitch)    { _piano->pressPitch(pitch);   }
       void releasePitch(int pitch)  { _piano->releasePitch(pitch); }
       void heartBeat(QList<const Note*> notes);
+      void changeSelection(Selection selection);
       };
 
 
index 9599edd..e8c85ad 100644 (file)
          <widget class="QPushButton" name="reloadPlugins">
           <property name="toolTip">
            <string>Reload all plugins.
-This will re-evaluate all plugins, picking up any changes that may have occured.</string>
+This will re-evaluate all plugins, picking up any changes that may have occurred.</string>
           </property>
           <property name="text">
            <string>Reload Plugins</string>
index d500971..46b22b7 100644 (file)
@@ -1 +1 @@
-a5ee1d7
+df52fba
index 523cbd9..9fc98c3 100644 (file)
@@ -1136,14 +1136,8 @@ void ScoreView::measurePopup(const QPoint& gpos, Measure* obj)
       a->setData("edit-drumset");
       a->setEnabled(staff->part()->instrument()->drumset() != 0);
 
-      if (staff->part()->instrument()->drumset()) {
-            a = popup->addAction(tr("Drumroll Editor..."));
-            a->setData("drumroll");
-            }
-      else {
-            a = popup->addAction(tr("Pianoroll Editor..."));
-            a->setData("pianoroll");
-            }
+      a = popup->addAction(tr("Piano Roll Editor..."));
+      a->setData("pianoroll");
 
       a = popup->addAction(tr("Staff Properties..."));
       a->setData("staff-properties");
@@ -4797,6 +4791,10 @@ void ScoreView::cmdTuplet(int n, ChordRest* cr)
             return;
             }
 
+      Measure* measure = cr->measure();
+      if (measure && measure->isMMRest())
+            return;
+
       Fraction f(cr->duration());
       int tick    = cr->tick();
       Tuplet* ot  = cr->tuplet();
@@ -4819,6 +4817,11 @@ void ScoreView::cmdTuplet(int n, ChordRest* cr)
       //    Example: an 1/8 has 240 midi ticks, in an 1/8 triplet the note
       //             has a tick duration of 240 / (3/2) = 160 ticks
       //
+      if (fr.denominator() > 128) {
+            delete tuplet;
+            mscore->noteTooShortForTupletDialog();
+            return;
+            }
 
       tuplet->setDuration(f);
       TDuration baseLen(fr);
@@ -4826,7 +4829,6 @@ void ScoreView::cmdTuplet(int n, ChordRest* cr)
 
       tuplet->setTrack(cr->track());
       tuplet->setTick(tick);
-      Measure* measure = cr->measure();
       tuplet->setParent(measure);
 
       if (ot)
index 013bc6b..fafad9a 100644 (file)
@@ -673,9 +673,9 @@ Shortcut Shortcut::_sc[] = {
          MsWidget::SCORE_TAB,
          STATE_NORMAL | STATE_NOTE_ENTRY,
          "stretch+",
-         QT_TRANSLATE_NOOP("action","Increase Measure Width"),
-         QT_TRANSLATE_NOOP("action","Increase measure width"),
-         QT_TRANSLATE_NOOP("action","Increase width of selected measures"),
+         QT_TRANSLATE_NOOP("action","Increase Layout Stretch"),
+         QT_TRANSLATE_NOOP("action","Increase layout stretch"),
+         QT_TRANSLATE_NOOP("action","Increase layout stretch factor of selected measures"),
          Icons::Invalid_ICON,
          Qt::WindowShortcut,
          ShortcutFlags::A_CMD
@@ -684,9 +684,9 @@ Shortcut Shortcut::_sc[] = {
          MsWidget::SCORE_TAB,
          STATE_NORMAL | STATE_NOTE_ENTRY,
          "stretch-",
-         QT_TRANSLATE_NOOP("action","Decrease Measure Width"),
-         QT_TRANSLATE_NOOP("action","Decrease measure width"),
-         QT_TRANSLATE_NOOP("action","Decrease width of selected measures"),
+         QT_TRANSLATE_NOOP("action","Decrease Layout Stretch"),
+         QT_TRANSLATE_NOOP("action","Decrease layout stretch"),
+         QT_TRANSLATE_NOOP("action","Decrease layout stretch factor of selected measures"),
          Icons::Invalid_ICON,
          Qt::WindowShortcut,
          ShortcutFlags::A_CMD
@@ -2305,9 +2305,9 @@ Shortcut Shortcut::_sc[] = {
          MsWidget::MAIN_WINDOW,
          STATE_NORMAL | STATE_NOTE_ENTRY,
          "reset-stretch",
-         QT_TRANSLATE_NOOP("action","Reset Measure Width"),
-         QT_TRANSLATE_NOOP("action","Reset measure width"),
-         0,
+         QT_TRANSLATE_NOOP("action","Reset Layout Stretch"),
+         QT_TRANSLATE_NOOP("action","Reset layout stretch"),
+         QT_TRANSLATE_NOOP("action","Reset layout stretch factor of selected measures or entire score"),
          Icons::Invalid_ICON,
          Qt::WindowShortcut,
          ShortcutFlags::A_CMD
diff --git a/mscore/stringutils.cpp b/mscore/stringutils.cpp
new file mode 100644 (file)
index 0000000..025b20b
--- /dev/null
@@ -0,0 +1,98 @@
+//=============================================================================
+//  MuseScore
+//  Linux Music Score Editor
+//  Copyright (C) 2002-2018 Werner Schweer and others
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License version 2.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//=============================================================================
+
+#include "stringutils.h"
+
+namespace Ms {
+
+//---------------------------------------------------------
+//   removeLigatures
+//---------------------------------------------------------
+
+QString stringutils::removeLigatures(const QString& pre)
+      {
+      QString result = pre;
+
+      // Characters with above dots (Ae, ae, Oe, oe, Ue, ue
+      result = result.replace(QRegularExpression("[\\x{00C4}]"), QString("Ae"));
+      result = result.replace(QRegularExpression("[\\x{00E4}]"), QString("ae"));
+      result = result.replace(QRegularExpression("[\\x{00D6}]"), QString("Oe"));
+      result = result.replace(QRegularExpression("[\\x{00F6}]"), QString("oe"));
+      result = result.replace(QRegularExpression("[\\x{00DC}]"), QString("Ue"));
+      result = result.replace(QRegularExpression("[\\x{00FC}]"), QString("ue"));
+
+      // Latin Big Letter AA (&#42802) and Latin Small Letter aa (&#42803)
+      result = result.replace(QRegularExpression("[\\x{A732}]"), QString("Aa"));
+      result = result.replace(QRegularExpression("[\\x{A733}]"), QString("aa"));
+
+      // Latin Big Letter AE (&#198) and Latin Small Letter ae (&#230)
+      result = result.replace(QRegularExpression("[\\x{00C6}]"), QString("Ae"));
+      result = result.replace(QRegularExpression("[\\x{00E6}]"), QString("ae"));
+
+      // Latin Big Letter AO (&#42804) and Latin Small Letter ao (&#42805)
+      result = result.replace(QRegularExpression("[\\x{A734}]"), QString("Ao"));
+      result = result.replace(QRegularExpression("[\\x{A735}]"), QString("ao"));
+
+      // Latin Big Letter AU (&#42806) and Latin Small Letter au (&#42807)
+      result = result.replace(QRegularExpression("[\\x{A736}]"), QString("Au"));
+      result = result.replace(QRegularExpression("[\\x{A737}]"), QString("au"));
+
+      // IJ (&#306) and ij (&#307)
+      result = result.replace(QRegularExpression("[\\x{0132}]"), QString("IJ"));
+      result = result.replace(QRegularExpression("[\\x{0133}]"), QString("ij"));
+
+      // Eszett SS (&#7838) and ss (&#223)
+      result = result.replace(QRegularExpression("[\\x{1E9E}]"), QString("SS"));
+      result = result.replace(QRegularExpression("[\\x{00DF}]"), QString("ss"));
+
+      // O with stroke (&#216) and o with stroke (&#248)
+      result = result.replace(QRegularExpression("[\\x{00D8}]"), QChar('O'));
+      result = result.replace(QRegularExpression("[\\x{00F8}]"), QChar('o'));
+
+      // Big Letter OE (&#338) and small letter oe (&#339)
+      result = result.replace(QRegularExpression("[\\x{0152}]"), QString("Oe"));
+      result = result.replace(QRegularExpression("[\\x{0153}]"), QString("oe"));
+
+      // Big Letter OO (&#42830) and small letter oo (&#42831)
+      result = result.replace(QRegularExpression("[\\x{A74E}]"), QString("Oo"));
+      result = result.replace(QRegularExpression("[\\x{A74F}]"), QString("oo"));
+
+      // ue (&#7531)
+      result = result.replace(QRegularExpression("[\\x{1D6B}]"), QString("ue"));
+
+      return result;
+      }
+
+//---------------------------------------------------------
+//   removeDiacritics
+//---------------------------------------------------------
+
+QString stringutils::removeDiacritics(const QString& pre)
+      {
+      QString text = pre.normalized(QString::NormalizationForm_KD);
+      QString result;
+      for (int i = 0; i < text.length(); ++i)  {
+            if (text.at(i).category() != QChar::Mark_NonSpacing) {
+                  result.append(text.at(i));
+                  }
+            }
+      result = result.normalized(QString::NormalizationForm_C);
+      return result;
+      }
+
+} // namespace Ms
diff --git a/mscore/stringutils.h b/mscore/stringutils.h
new file mode 100644 (file)
index 0000000..6328757
--- /dev/null
@@ -0,0 +1,37 @@
+//=============================================================================
+//  MuseScore
+//  Linux Music Score Editor
+//  Copyright (C) 2002-2018 Werner Schweer and others
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License version 2.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//=============================================================================
+
+#ifndef STRINGUTILS_H
+#define STRINGUTILS_H
+
+#include <QObject>
+
+namespace Ms {
+
+class stringutils : public QObject
+{
+      Q_OBJECT
+
+   public:
+      static QString removeLigatures(const QString& pre);
+      static QString removeDiacritics(const QString& pre);
+};
+
+} // namespace Ms
+
+#endif // STRINGUTILS_H
index 8b2e789..b739e65 100644 (file)
@@ -40,7 +40,6 @@
 ****************************************************************************/
 
 #include "svggenerator.h"
-#include "paintengine_p.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 // FOR GRADIENT FUNCTIONALITY THAT IS NOT IMPLEMENTED (YET):
@@ -152,7 +151,7 @@ static QString getClass(const Ms::Element *e)
     return eName;
 }
 
-class SvgPaintEnginePrivate : public QPaintEnginePrivate
+class SvgPaintEnginePrivate
 {
 public:
     SvgPaintEnginePrivate()
@@ -231,6 +230,7 @@ class SvgPaintEngine : public QPaintEngine
 private:
     QString     stateString;
     QTextStream stateStream;
+    SvgPaintEnginePrivate *d_ptr;
 
 // Qt translates everything. These help avoid SVG transform="translate()".
     qreal _dx;
@@ -311,10 +311,10 @@ protected:
 
 public:
     SvgPaintEngine()
-        : QPaintEngine(*new SvgPaintEnginePrivate,
-                       svgEngineFeatures()),
+        : QPaintEngine(svgEngineFeatures()),
           stateStream(&stateString)
     {
+        d_ptr = new SvgPaintEnginePrivate;
     }
 
     bool begin(QPaintDevice *device);
index 2f0b612..5628948 100644 (file)
@@ -85,13 +85,15 @@ void PositionCursor::paint(QPainter* p)
                   break;
             default:                            // fill the rectangle and add TAB string marks, if required                  
                   p->fillRect(_rect, color());
-                  int         track       = _sv->score()->inputTrack();
-                  Staff*      staff       = _sv->score()->staff(track2staff(track));
-                  if (staff) {
-                        StaffType*  staffType   = staff->staffType();
-                        if (staffType && staffType->group() == StaffGroup::TAB)
-                              staffType->drawInputStringMarks(p, _sv->score()->inputState().string(),
-                                          track2voice(track), _rect);
+                  if (_sv->score()->noteEntryMode()) {
+                        int         track       = _sv->score()->inputTrack();
+                        Staff*      staff       = _sv->score()->staff(track2staff(track));
+                        if (staff) {
+                              StaffType*  staffType   = staff->staffType();
+                              if (staffType && staffType->group() == StaffGroup::TAB)
+                                    staffType->drawInputStringMarks(p, _sv->score()->inputState().string(),
+                                                track2voice(track), _rect);
+                              }
                         }
                   break;
             }
index 47caa09..2df4cfb 100644 (file)
@@ -94,6 +94,11 @@ Tuplet* MuseScore::tupletDialog()
             noteTooShortForTupletDialog();
             return 0;
             }
+
+      Measure* measure = cr->measure();
+      if (measure && measure->isMMRest())
+            return 0;
+
       TupletDialog td;
       if (!td.exec())
             return 0;
@@ -113,6 +118,12 @@ Tuplet* MuseScore::tupletDialog()
          qPrintable(tuplet->ratio().print()),
          qPrintable(f.print()));
 
+      if (f.denominator() > 128) {
+            delete tuplet;
+            mscore->noteTooShortForTupletDialog();
+            return 0;
+            }
+
       tuplet->setBaseLen(Fraction(1, f.denominator()));
 
       if (tuplet->baseLen() == TDuration::DurationType::V_INVALID) {
@@ -123,7 +134,6 @@ Tuplet* MuseScore::tupletDialog()
             return 0;
             }
 
-      Measure* measure = cr->measure();
       tuplet->setParent(measure);
 
       return tuplet;
index 174ee84..3362da2 100644 (file)
@@ -81,6 +81,8 @@ add_library(
       ${PROJECT_SOURCE_DIR}/mscore/exportmidi.cpp
       ${PROJECT_SOURCE_DIR}/mscore/importmxml.cpp               # Required by importxml.cpp
       ${PROJECT_SOURCE_DIR}/mscore/importmxmllogger.cpp         # Required by importxml.cpp
+      ${PROJECT_SOURCE_DIR}/mscore/importmxmlnoteduration.cpp   # Required by importxml.cpp
+      ${PROJECT_SOURCE_DIR}/mscore/importmxmlnotepitch.cpp      # Required by importxml.cpp
       ${PROJECT_SOURCE_DIR}/mscore/importmxmlpass1.cpp          # Required by importxml.cpp
       ${PROJECT_SOURCE_DIR}/mscore/importmxmlpass2.cpp          # Required by importxml.cpp
       ${PROJECT_SOURCE_DIR}/mscore/importxml.cpp
@@ -97,6 +99,7 @@ add_library(
       ${PROJECT_SOURCE_DIR}/thirdparty/beatroot/AgentList.cpp       # required by importmidi.cpp
       ${PROJECT_SOURCE_DIR}/thirdparty/beatroot/BeatTracker.cpp     # required by importmidi.cpp
       ${PROJECT_SOURCE_DIR}/thirdparty/beatroot/Induction.cpp       # required by importmidi.cpp
+      ${PROJECT_SOURCE_DIR}/mscore/stringutils.cpp
       ${OMR_SRC}
       omr
       )
@@ -157,7 +160,7 @@ add_custom_target(reporthtml
       WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/mtest"
       )
 
-subdirs (libmscore importmidi capella biab musicxml guitarpro scripting testoves zerberus)
+subdirs (libmscore importmidi capella biab musicxml guitarpro scripting testoves zerberus stringutils)
 
 install(FILES
       ../share/styles/chords_std.xml
diff --git a/mtest/libmscore/repeat/repeat47.mscx b/mtest/libmscore/repeat/repeat47.mscx
new file mode 100644 (file)
index 0000000..89f9119
--- /dev/null
@@ -0,0 +1,768 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<museScore version="2.06">
+  <programVersion>2.1.0</programVersion>
+  <programRevision>871c8ce</programRevision>
+  <Score>
+    <LayerTag id="0" tag="default"></LayerTag>
+    <currentLayer>0</currentLayer>
+    <Synthesizer>
+      </Synthesizer>
+    <Division>480</Division>
+    <Style>
+      <lastSystemFillLimit>0</lastSystemFillLimit>
+      <page-layout>
+        <page-height>1683.36</page-height>
+        <page-width>1190.88</page-width>
+        <page-margins type="even">
+          <left-margin>56.6929</left-margin>
+          <right-margin>56.6929</right-margin>
+          <top-margin>56.6929</top-margin>
+          <bottom-margin>113.386</bottom-margin>
+          </page-margins>
+        <page-margins type="odd">
+          <left-margin>56.6929</left-margin>
+          <right-margin>56.6929</right-margin>
+          <top-margin>56.6929</top-margin>
+          <bottom-margin>113.386</bottom-margin>
+          </page-margins>
+        </page-layout>
+      <Spatium>1.76389</Spatium>
+      </Style>
+    <showInvisible>1</showInvisible>
+    <showUnprintable>1</showUnprintable>
+    <showFrames>1</showFrames>
+    <showMargins>0</showMargins>
+    <metaTag name="arranger"></metaTag>
+    <metaTag name="composer"></metaTag>
+    <metaTag name="copyright"></metaTag>
+    <metaTag name="creationDate">2018-02-12</metaTag>
+    <metaTag name="lyricist"></metaTag>
+    <metaTag name="movementNumber"></metaTag>
+    <metaTag name="movementTitle"></metaTag>
+    <metaTag name="platform">Microsoft Windows</metaTag>
+    <metaTag name="poet"></metaTag>
+    <metaTag name="source"></metaTag>
+    <metaTag name="translator"></metaTag>
+    <metaTag name="workNumber"></metaTag>
+    <metaTag name="workTitle"></metaTag>
+    <PageList>
+      <Page>
+        <System>
+          </System>
+        <System>
+          </System>
+        <System>
+          </System>
+        <System>
+          </System>
+        <System>
+          </System>
+        <System>
+          </System>
+        </Page>
+      </PageList>
+    <Part>
+      <Staff id="1">
+        <StaffType group="pitched">
+          <name>stdNormal</name>
+          </StaffType>
+        </Staff>
+      <trackName>Piano</trackName>
+      <Instrument>
+        <longName>Piano</longName>
+        <shortName>Pno.</shortName>
+        <trackName>Piano</trackName>
+        <minPitchP>21</minPitchP>
+        <maxPitchP>108</maxPitchP>
+        <minPitchA>21</minPitchA>
+        <maxPitchA>108</maxPitchA>
+        <instrumentId>keyboard.piano</instrumentId>
+        <clef staff="2">F</clef>
+        <Articulation>
+          <velocity>100</velocity>
+          <gateTime>95</gateTime>
+          </Articulation>
+        <Articulation name="staccatissimo">
+          <velocity>100</velocity>
+          <gateTime>33</gateTime>
+          </Articulation>
+        <Articulation name="staccato">
+          <velocity>100</velocity>
+          <gateTime>50</gateTime>
+          </Articulation>
+        <Articulation name="portato">
+          <velocity>100</velocity>
+          <gateTime>67</gateTime>
+          </Articulation>
+        <Articulation name="tenuto">
+          <velocity>100</velocity>
+          <gateTime>100</gateTime>
+          </Articulation>
+        <Articulation name="marcato">
+          <velocity>120</velocity>
+          <gateTime>67</gateTime>
+          </Articulation>
+        <Articulation name="sforzato">
+          <velocity>120</velocity>
+          <gateTime>100</gateTime>
+          </Articulation>
+        <Channel>
+          <program value="0"/>
+          <synti>Fluid</synti>
+          </Channel>
+        </Instrument>
+      </Part>
+    <Staff id="1">
+      <Measure number="1">
+        <TimeSig>
+          <sigN>4</sigN>
+          <sigD>4</sigD>
+          <showCourtesySig>1</showCourtesySig>
+          </TimeSig>
+        <Tempo>
+          <tempo>4</tempo>
+          <followText>1</followText>
+          <text><sym>unicodeNoteQuarterUp</sym> = 240</text>
+          </Tempo>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="2">
+        <startRepeat/>
+        <Marker>
+          <style>Repeat Text Left</style>
+          <text><sym>segno</sym><sup>1</sup></text>
+          <label>segno</label>
+          </Marker>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <BarLine>
+          <subtype>double</subtype>
+          <span>1</span>
+          </BarLine>
+        </Measure>
+      <Measure number="3">
+        <endRepeat>2</endRepeat>
+        <Volta id="2">
+          <endHook>1</endHook>
+          <beginText>
+            <text>1.</text>
+            </beginText>
+          <endings>1</endings>
+          </Volta>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="4">
+        <Marker>
+          <pos x="0" y="-3.5"/>
+          <style>Repeat Text Right</style>
+          <text>To Codab</text>
+          <label>coda</label>
+          </Marker>
+        <endSpanner id="2"/>
+        <Volta id="3">
+          <beginText>
+            <text>2.</text>
+            </beginText>
+          <endings>2</endings>
+          </Volta>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <HBox>
+        <width>5</width>
+        </HBox>
+      <Measure number="5">
+        <endSpanner id="3"/>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="6">
+        <startRepeat/>
+        <LayoutBreak>
+          <subtype>line</subtype>
+          </LayoutBreak>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="7">
+        <endRepeat>2</endRepeat>
+        <Volta id="4">
+          <endHook>1</endHook>
+          <beginText>
+            <text>1.</text>
+            </beginText>
+          <endings>1</endings>
+          </Volta>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="8">
+        <endSpanner id="4"/>
+        <Volta id="5">
+          <beginText>
+            <text>2.</text>
+            </beginText>
+          <endings>2</endings>
+          </Volta>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="9">
+        <Marker>
+          <pos x="1.1" y="-2"/>
+          <style>Repeat Text Left</style>
+          <text><sym>segno</sym><sup>2</sup></text>
+          <label>segno2</label>
+          </Marker>
+        <endSpanner id="5"/>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="10">
+        <Marker>
+          <style>Repeat Text Right</style>
+          <text>To Codab1</text>
+          <label>coda1</label>
+          </Marker>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <BarLine>
+          <subtype>double</subtype>
+          <span>1</span>
+          </BarLine>
+        </Measure>
+      <HBox>
+        <width>5</width>
+        </HBox>
+      <Measure number="11">
+        <Jump>
+          <style>Repeat Text Right</style>
+          <text>D.S. al Codab</text>
+          <jumpTo>segno</jumpTo>
+          <playUntil>coda</playUntil>
+          <continueAt>codab</continueAt>
+          </Jump>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <BarLine>
+          <subtype>double</subtype>
+          <span>1</span>
+          </BarLine>
+        </Measure>
+      <Measure number="12">
+        <LayoutBreak>
+          <subtype>line</subtype>
+          </LayoutBreak>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <BarLine>
+          <subtype>double</subtype>
+          <span>1</span>
+          </BarLine>
+        </Measure>
+      <Measure number="13">
+        <Marker>
+          <pos x="1.058" y="-2"/>
+          <style>Repeat Text Left</style>
+          <text><sym>coda</sym><sup>b</sup></text>
+          <label>codab</label>
+          </Marker>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="14">
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        </Measure>
+      <Measure number="15">
+        <Jump>
+          <style>Repeat Text Right</style>
+          <text>D.S. al Codab1</text>
+          <jumpTo>segno2</jumpTo>
+          <playUntil>coda1</playUntil>
+          <continueAt>codab1</continueAt>
+          </Jump>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+            <tpc>17</tpc>
+            </Note>
+          </Chord>
+        <BarLine>
+          <subtype>double</subtype>
+          <span>1</span>
+          </BarLine>
+        </Measure>
+      <HBox>
+        <width>5</width>
+        </HBox>
+      <Measure number="16">
+        <Marker>
+          <pos x="0.958002" y="-2"/>
+          <style>Repeat Text Left</style>
+          <text><sym>coda</sym><sup>b1</sup></text>
+          <label>codab1</label>
+          </Marker>
+        <Chord>
+          <durationType>quarter</durationType>
+          <Note>
+            <pitch>69</pitch>
+       &