LCOV - code coverage report
Current view: top level - src/engraving/dom - repeatlist.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 93.3 % 524 489
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "repeatlist.h"
      24              : 
      25              : #include <list>
      26              : #include <set>
      27              : #include <utility> // std::pair
      28              : 
      29              : #include "jump.h"
      30              : #include "layoutbreak.h"
      31              : #include "marker.h"
      32              : #include "measure.h"
      33              : #include "score.h"
      34              : #include "volta.h"
      35              : 
      36              : #include "global/realfn.h"
      37              : #include "global/log.h"
      38              : 
      39              : using namespace mu;
      40              : 
      41              : namespace mu::engraving {
      42              : //---------------------------------------------------------
      43              : //   RepeatSegment
      44              : //---------------------------------------------------------
      45              : 
      46         2616 : RepeatSegment::RepeatSegment(int playbackCount)
      47         2616 :     : tick(0), utick(0), pause(0.0), playbackCount(playbackCount)
      48              : {
      49         2616 : }
      50              : 
      51         6313 : void RepeatSegment::addMeasure(Measure const* const m)
      52              : {
      53         6313 :     if (m != nullptr) {
      54         6313 :         if (m_measureList.empty()) {
      55         3632 :             tick = m->tick().ticks();
      56              :         }
      57         6313 :         if ((m_measureList.empty()) || (m_measureList.back() != m)) {
      58         6313 :             m_measureList.push_back(m);
      59              :         }
      60              :     }
      61         6313 : }
      62              : 
      63              : /// \brief Expands or clips my measureList up to m (including m)
      64         3697 : void RepeatSegment::addMeasures(Measure const* const m)
      65              : {
      66         3697 :     if (!m_measureList.empty()) {
      67              :         // Add up to the current measure, final measure is added outside of this condition
      68         3697 :         Measure const* lastMeasure = m_measureList.back()->nextMeasureMM();
      69         3697 :         if (lastMeasure && (lastMeasure->tick() < m->tick())) {     // Ensure provided reference is later than current last
      70        11867 :             while (lastMeasure && lastMeasure != m) {
      71        10578 :                 m_measureList.push_back(lastMeasure);
      72        10578 :                 lastMeasure = lastMeasure->nextMeasureMM();
      73              :             }
      74              :         }
      75              :         //else { // Possibly clip compared to current last measure }
      76         4933 :         while (!m_measureList.empty() && (m_measureList.back()->tick() >= m->tick())) {
      77         1236 :             m_measureList.pop_back();
      78              :         }
      79              :     }
      80         3697 :     addMeasure(m);
      81         3697 : }
      82              : 
      83         3856 : bool RepeatSegment::containsMeasure(Measure const* const m) const
      84              : {
      85        24806 :     for (Measure const* const measure : m_measureList) {
      86        24592 :         if (measure == m) {
      87         3642 :             return true;
      88              :         }
      89              :     }
      90          214 :     return false;
      91              : }
      92              : 
      93        42539 : bool RepeatSegment::endsWithMeasure(Measure const* const m) const
      94              : {
      95        42539 :     if (m_measureList.empty()) {
      96            0 :         return false;
      97              :     }
      98              : 
      99        42539 :     return m_measureList.back() == m;
     100              : }
     101              : 
     102           53 : bool RepeatSegment::startsWithMeasure(const Measure* const m) const
     103              : {
     104           53 :     if (m_measureList.empty()) {
     105            0 :         return false;
     106              :     }
     107              : 
     108           53 :     return m_measureList.front() == m;
     109              : }
     110              : 
     111         2616 : bool RepeatSegment::isEmpty() const
     112              : {
     113         2616 :     return m_measureList.empty();
     114              : }
     115              : 
     116         5257 : int RepeatSegment::len() const
     117              : {
     118         5257 :     return (m_measureList.empty()) ? 0 : (m_measureList.back()->endTick().ticks() - tick);
     119              : }
     120              : 
     121         1901 : int RepeatSegment::endTick() const
     122              : {
     123         1901 :     return tick + len();
     124              : }
     125              : 
     126          258 : void RepeatSegment::popMeasure()
     127              : {
     128          258 :     if (!m_measureList.empty()) {
     129          258 :         m_measureList.pop_back();
     130              :     }
     131          258 : }
     132              : 
     133          599 : const std::vector<const Measure*>& RepeatSegment::measureList() const
     134              : {
     135          599 :     return m_measureList;
     136              : }
     137              : 
     138          698 : bool operator==(const RepeatSegment& lhs, const RepeatSegment& rhs)
     139              : {
     140          698 :     if (lhs.m_measureList.size() != rhs.m_measureList.size()) {
     141          314 :         return false;
     142              :     }
     143         3974 :     for (size_t i = 0; i < lhs.m_measureList.size(); i++) {
     144         3591 :         if (lhs.m_measureList.at(i) != rhs.m_measureList.at(i)) {
     145            1 :             return false;
     146              :         }
     147              :     }
     148          383 :     return lhs.tick == rhs.tick
     149          383 :            && lhs.utick == rhs.utick
     150          383 :            && muse::RealIsEqual(lhs.pause, rhs.pause)
     151          766 :            && lhs.playbackCount == rhs.playbackCount;
     152              : }
     153              : 
     154              : //---------------------------------------------------------
     155              : //   RepeatList
     156              : //---------------------------------------------------------
     157              : 
     158         4338 : RepeatList::RepeatList(Score* s)
     159              : {
     160         4338 :     m_score = s;
     161         4338 :     m_utick2tickHint = 0;
     162         4338 : }
     163              : 
     164              : //---------------------------------------------------------
     165              : //   ~RepeatList
     166              : //---------------------------------------------------------
     167              : 
     168         3888 : RepeatList::~RepeatList()
     169              : {
     170         3888 :     muse::DeleteAll(*this);
     171         3888 : }
     172              : 
     173              : //---------------------------------------------------------
     174              : //   ticks
     175              : //---------------------------------------------------------
     176              : 
     177            0 : int RepeatList::ticks() const
     178              : {
     179            0 :     if (size() > 0) {
     180            0 :         const RepeatSegment* s = back();
     181            0 :         return s->utick + s->len();
     182              :     }
     183            0 :     return 0;
     184              : }
     185              : 
     186              : //---------------------------------------------------------
     187              : //   update
     188              : //---------------------------------------------------------
     189              : 
     190        37886 : void RepeatList::update(bool expand, bool updateTies)
     191              : {
     192        37886 :     if (!m_scoreChanged && expand == m_expanded) {
     193        36079 :         return;
     194              :     }
     195              : 
     196         1807 :     std::vector<RepeatSegment> oldSegments;
     197         1807 :     oldSegments.reserve(size());
     198         2534 :     for (RepeatSegment* rs : *this) {
     199          727 :         oldSegments.push_back(*rs);
     200              :     }
     201              : 
     202         1807 :     if (expand) {
     203         1807 :         unwind();
     204              :     } else {
     205            0 :         flatten();
     206              :     }
     207              : 
     208         1807 :     bool structureChanged = (oldSegments.size() != size());
     209         1807 :     if (!structureChanged) {
     210          967 :         for (size_t i = 0; i < size(); ++i) {
     211          698 :             if (!(oldSegments[i] == *at(i))) {
     212          315 :                 structureChanged = true;
     213          315 :                 break;
     214              :             }
     215              :         }
     216              :     }
     217              : 
     218         1807 :     m_scoreChanged = false;
     219              : 
     220         1807 :     if (updateTies && structureChanged) {
     221            0 :         m_score->undoRemoveStaleTieJumpPoints();
     222              :     }
     223         1807 : }
     224              : 
     225         1759 : void RepeatList::updateUticks()
     226              : {
     227         1759 :     int utick = 0;
     228              : 
     229         4317 :     for (RepeatSegment* s : *this) {
     230         2558 :         s->utick = utick;
     231         2558 :         utick += s->len();
     232              :     }
     233         1759 : }
     234              : 
     235              : //---------------------------------------------------------
     236              : //   utick2tick
     237              : //---------------------------------------------------------
     238              : 
     239           13 : int RepeatList::utick2tick(int tick) const
     240              : {
     241           13 :     size_t n = size();
     242           13 :     if (n == 0) {
     243            0 :         return tick;
     244              :     }
     245           13 :     if (tick < 0) {
     246            0 :         return 0;
     247              :     }
     248           13 :     unsigned ii = (m_utick2tickHint < n) && (tick >= at(m_utick2tickHint)->utick) ? m_utick2tickHint : 0;
     249           13 :     for (unsigned i = ii; i < n; ++i) {
     250           13 :         if ((tick >= at(i)->utick) && ((i + 1 == n) || (tick < at(i + 1)->utick))) {
     251           13 :             m_utick2tickHint = i;
     252           13 :             return tick - (at(i)->utick - at(i)->tick);
     253              :         }
     254              :     }
     255              : 
     256            0 :     ASSERT_X(String(u"tick %1 not found in RepeatList").arg(tick));
     257              :     return 0;
     258              : }
     259              : 
     260              : //---------------------------------------------------------
     261              : //   tick2utick
     262              : //---------------------------------------------------------
     263              : 
     264            7 : int RepeatList::tick2utick(int tick) const
     265              : {
     266            7 :     if (empty()) {
     267            0 :         return 0;
     268              :     }
     269            7 :     for (const RepeatSegment* s : *this) {
     270            7 :         if (tick >= s->tick && tick < s->endTick()) {
     271            7 :             return s->utick + (tick - s->tick);
     272              :         }
     273              :     }
     274            0 :     return back()->utick + (tick - back()->tick);
     275              : }
     276              : 
     277              : ///
     278              : /// \brief Lookup the RepeatSegment containing the given utick
     279              : ///
     280          582 : std::vector<RepeatSegment*>::const_iterator RepeatList::findRepeatSegmentFromUTick(int utick) const
     281              : {
     282          617 :     for (auto it = cbegin(); it != cend(); ++it) {
     283          617 :         const RepeatSegment* seg = *it;
     284          617 :         if (utick >= seg->utick && utick < seg->utick + seg->len()) {
     285          582 :             return it;
     286              :         }
     287              :     }
     288              : 
     289            0 :     return cend();
     290              : }
     291              : 
     292          487 : std::vector<RepeatSegmentInfo> RepeatList::segmentInfoList() const
     293              : {
     294          487 :     std::vector<RepeatSegmentInfo> result;
     295          487 :     result.reserve(size());
     296              : 
     297          941 :     for (const RepeatSegment* seg : *this) {
     298          454 :         result.push_back({ seg->tick, seg->endTick(), seg->utick });
     299              :     }
     300              : 
     301          487 :     return result;
     302            0 : }
     303              : 
     304              : //---------------------------------------------------------
     305              : //   flatten
     306              : ///   Make this repeat list flat (don't expand repeats)
     307              : //---------------------------------------------------------
     308              : 
     309            0 : void RepeatList::flatten()
     310              : {
     311            0 :     muse::DeleteAll(*this);
     312            0 :     clear();
     313              : 
     314            0 :     Measure* m = m_score->firstMeasureMM();
     315            0 :     if (!m) {
     316            0 :         return;
     317              :     }
     318              : 
     319            0 :     RepeatSegment* s = new RepeatSegment(1);
     320              :     do {
     321            0 :         s->addMeasure(m);
     322            0 :         m = m->nextMeasureMM();
     323            0 :     } while (m);
     324            0 :     push_back(s);
     325              : 
     326            0 :     m_expanded = false;
     327              : }
     328              : 
     329              : //---------------------------------------------------------
     330              : //   unwind
     331              : //    implements:
     332              : //          - repeats
     333              : //          - volta
     334              : //          - d.c. al fine
     335              : //          - d.s. al fine
     336              : //          - d.s. al coda
     337              : //---------------------------------------------------------
     338              : enum class RepeatListElementType : unsigned char {
     339              :     SECTION_BREAK,
     340              :     VOLTA_START,
     341              :     VOLTA_END,
     342              :     REPEAT_START,
     343              :     REPEAT_END,
     344              :     JUMP,
     345              :     MARKER
     346              : };
     347              : 
     348              : class RepeatListElement
     349              : {
     350              : public:
     351              :     RepeatListElementType const repeatListElementType;
     352              :     EngravingItem const* const element;
     353              :     Measure const* const measure;    // convenience, should be measure containing element
     354              : private:
     355              :     int repeatCount;
     356              : 
     357              : public:
     358         4939 :     RepeatListElement(RepeatListElementType type, EngravingItem const* const el, Measure const* const m)
     359         4939 :         : repeatListElementType(type), element(el), measure(m)
     360              :     {
     361         4939 :         repeatCount = (type == RepeatListElementType::REPEAT_START) ? 1 : 0;
     362         4939 :     }
     363              : 
     364         1429 :     ~RepeatListElement()
     365              :     {
     366              :         // Voltas are cloned elements, we need to cleanup ourselves
     367         1429 :         if (repeatListElementType == RepeatListElementType::VOLTA_START) {
     368           58 :             delete element;
     369              :         }
     370         1429 :     }
     371              : 
     372         1587 :     int getRepeatCount() const { return repeatCount; }
     373         1011 :     void addToRepeatCount(int add) { repeatCount += add; }
     374              : };
     375              : 
     376              : ///---------------------------------------------------------
     377              : /// \brief Collects all RepeatListElementType elements in the score
     378              : /// \details Those are the only type of elements that actually influence playback order
     379              : ///---------------------------------------------------------
     380         1759 : void RepeatList::collectRepeatListElements()
     381              : {
     382         1759 :     RepeatListElementList sectionRLElements;
     383              : 
     384              :     // Clear out previous listing
     385         2327 :     for (const RepeatListElementList& srle : m_rlElements) {
     386          568 :         muse::DeleteAll(srle);
     387              :     }
     388         1759 :     m_rlElements.clear();
     389              : 
     390         1759 :     RepeatListElement* startFromRepeatMeasure = nullptr;
     391              : 
     392              :     // Section breaks may occur on non-Measure frames, so must search list of all MeasureBases
     393              :     // unwinding itself will only use actual Measures
     394         1759 :     MeasureBase* mb = m_score->firstMeasureMM();
     395              :     // First measure of a section/score is always used as a reference REPEAT_START point
     396              :     // even if it doesn't have a start repeat
     397         1759 :     startFromRepeatMeasure = new RepeatListElement(RepeatListElementType::REPEAT_START, mb, toMeasure(mb));
     398         1759 :     sectionRLElements.push_back(startFromRepeatMeasure);
     399              :     // Also trace down the final actual measure of this section (in case a frame follows)
     400         1759 :     MeasureBase* sectionEndMeasureBase = nullptr;
     401              :     // Used to track voltas; overlappings and real endings
     402              :     Volta* volta;
     403         1759 :     std::list<Volta*> preProcessedVoltas;
     404              : 
     405              :     // Voltas might overlap (duplicate entries on multiple staves or "real" overlaps)
     406              :     // so we will pre-process them into cloned versions that handle those overlaps.
     407              :     // This assumes that spanners are ordered from first to last tick-wise
     408         3048 :     for (const auto& spannerEntry : m_score->spanner()) {
     409         1289 :         if (!spannerEntry.second->isVolta() || !spannerEntry.second->playSpanner()) {
     410         1055 :             continue;
     411              :         }
     412              : 
     413          234 :         if (!spannerEntry.second->startMeasure() || !spannerEntry.second->endMeasure()) {
     414           16 :             continue;
     415              :         }
     416              : 
     417          218 :         volta = toVolta(spannerEntry.second)->clone();
     418              : 
     419          218 :         if (preProcessedVoltas.empty()) { // First entry
     420           92 :             preProcessedVoltas.push_back(volta);
     421              :         } else { // Compare
     422          126 :             std::list<Volta*> voltasToMerge;
     423          126 :             Fraction startMeasureTick = volta->startMeasure()->tick();
     424              : 
     425              :             // List all overlapping voltas
     426          138 :             while ((!preProcessedVoltas.empty())
     427          138 :                    && (startMeasureTick <= preProcessedVoltas.back()->endMeasure()->tick())
     428              :                    ) {
     429           12 :                 voltasToMerge.push_back(preProcessedVoltas.back());
     430           12 :                 preProcessedVoltas.pop_back();
     431              :             }
     432              : 
     433          138 :             while (!voltasToMerge.empty()) {
     434              :                 // We'll have to shorten the already stored volta and split its remainder for merging
     435           12 :                 Volta* remainder = voltasToMerge.back()->clone();
     436           12 :                 if (volta->startMeasure() != remainder->startMeasure()) {
     437              :                     // First part is not empty
     438            5 :                     voltasToMerge.back()->setEndElement(volta->startMeasure()->prevMeasureMM());
     439            5 :                     remainder->setStartElement(volta->startMeasure());
     440              :                     // Store it
     441            5 :                     preProcessedVoltas.push_back(voltasToMerge.back());
     442              :                 }
     443              :                 //else { New volta and existing one start at the same moment, there is no first part, only a remainder }
     444           12 :                 voltasToMerge.pop_back();
     445              : 
     446              :                 // remainder and volta now have the same start point
     447              :                 // Compare the end points and make remainder end first
     448           12 :                 if (volta->endMeasure()->tick() < remainder->endMeasure()->tick()) {
     449            3 :                     Volta* swap = volta;
     450            3 :                     volta = remainder;
     451            3 :                     remainder = swap;
     452              :                 }
     453              :                 // Cross-section of the repeatList
     454           12 :                 std::vector<int> endings = remainder->endings();
     455           12 :                 muse::remove_if(endings, [&volta](const int& ending) {
     456           20 :                     return !(volta->hasEnding(ending));
     457              :                 });
     458              : 
     459           12 :                 remainder->setEndings(endings);
     460              :                 // Split and merge done
     461           12 :                 preProcessedVoltas.push_back(remainder);
     462           12 :                 if (volta->endMeasure() != remainder->endMeasure()) {
     463              :                     // volta extends past the end of remainder -> move its startpoint after remainder
     464            6 :                     volta->setStartElement(remainder->endMeasure()->nextMeasureMM());
     465              :                 } else {         // volta matched remainder endpoint, nothing left to merge from
     466            6 :                     preProcessedVoltas.splice(preProcessedVoltas.cend(), voltasToMerge);
     467            6 :                     delete volta;
     468            6 :                     volta = nullptr;
     469              :                 }
     470           12 :             } // !voltasToMerge.empty()
     471              : 
     472          126 :             if (volta != nullptr) {
     473          120 :                 preProcessedVoltas.push_back(volta);
     474              :             }
     475          126 :         }
     476              :     }
     477              : 
     478         1759 :     volta = nullptr;
     479        14470 :     for (; mb; mb = mb->nextMM()) {
     480        14470 :         if (mb->isMeasure()) {
     481        14398 :             Measure* m = toMeasure(mb);
     482        14398 :             sectionEndMeasureBase = mb; // ending measure of section is the most recently encountered actual Measure
     483        14398 :             Measure* underlyingMeasure = m->isMMRest() ? m->mmRestFirst() : m;
     484              : 
     485              :             // Volta ?
     486        14398 :             if ((!preProcessedVoltas.empty()) && (preProcessedVoltas.front()->startMeasure() == underlyingMeasure)) {
     487          217 :                 if (volta != nullptr) {
     488              :                     //if (volta->endMeasure()->tick() < m->tick()) {
     489              :                     // The previous volta was supposed to end before us (open volta case) -> insert the end
     490            5 :                     sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::VOLTA_END, volta, m->prevMeasureMM()));
     491              :                     //volta = nullptr; // No need, replaced immediately further down
     492              :                     //      }
     493              :                     //else { // Overlapping voltas; this should not happen as preProcessedVoltas should've dealt with this already }
     494              :                 }
     495              :                 // Now insert the start of the current volta
     496          217 :                 volta = preProcessedVoltas.front();
     497          217 :                 sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::VOLTA_START, volta, m));
     498              :                 // Look for start of next volta
     499          217 :                 preProcessedVoltas.pop_front();
     500              :             }
     501              :             // Start
     502        14398 :             if (m->repeatStart()) {
     503          169 :                 if (volta != nullptr) {
     504            7 :                     if (volta->startMeasure() != underlyingMeasure) {
     505              :                         // Volta and Start repeat are not on the same measure
     506              :                         // assume the previous volta was supposed to end before us (open volta case) -> insert the end
     507              :                         // Warning: This might "break" a volta prematurely if its explicit notated end is later than this point
     508              :                         //          Consider splitting the volta or ignoring this repeat all together
     509            5 :                         sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::VOLTA_END, volta, m->prevMeasureMM()));
     510            5 :                         volta = nullptr;
     511              :                     }
     512              :                     //else { // Volta and Start Repeat coincide on the same measure, see test::repeat56.mscx }
     513              :                 }
     514          169 :                 startFromRepeatMeasure = new RepeatListElement(RepeatListElementType::REPEAT_START, m, m);
     515          169 :                 sectionRLElements.push_back(startFromRepeatMeasure);
     516              :             }
     517              :             // Jumps and Markers
     518        16167 :             for (EngravingItem* e : m->el()) {
     519         1769 :                 if (e->systemFlag() && !e->isTopSystemObject()) {
     520            2 :                     continue;
     521              :                 }
     522              : 
     523         1767 :                 if (e->isJump()) {
     524          188 :                     sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::JUMP, e, m));
     525          188 :                     if (volta != nullptr) {
     526           22 :                         if ((volta->endMeasure()->tick() < m->tick())
     527           57 :                             || ((volta->endMeasure()->tick() == m->tick())
     528           35 :                                 && (volta->getProperty(Pid::END_HOOK_TYPE).value<HookType>() == HookType::NONE)
     529              :                                 )
     530              :                             ) {
     531              :                             // The previous volta was supposed to end before us
     532              :                             // or open volta ends together with us -> insert the end
     533            8 :                             if (!m->repeatEnd()) {
     534              :                                 // But only do so if this measure doesn't also have an end repeat: see #327681
     535            7 :                                 sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::VOLTA_END, volta, m));
     536            7 :                                 volta = nullptr;
     537              :                             }
     538              :                             //else {
     539              :                             //    The measure also has an end repeat which should be included in the volta
     540              :                             //    We can't abort the volta here, and leave it to the end repeat to do so
     541              :                             //}
     542              :                         }
     543              :                         //else { // Volta is spanning past this jump instruction }
     544              :                     }
     545         1579 :                 } else if (e->isMarker()) {
     546          273 :                     RepeatListElement* markerRLE = new RepeatListElement(RepeatListElementType::MARKER, e, m);
     547              :                     // There may be multiple markers in the same measure. Make sure we place right markers before left
     548              :                     // At the same time, we should ensure Markers are evaluated before Jumps
     549          273 :                     bool markerRLEisRight = toMarker(e)->isRightMarker();
     550          273 :                     auto insertionIt = sectionRLElements.end() - 1;
     551          275 :                     while ((*insertionIt)->measure == markerRLE->measure) {
     552           52 :                         bool markerShouldGoBefore = false;
     553           52 :                         if (((*insertionIt)->repeatListElementType == RepeatListElementType::MARKER)
     554           52 :                             && (!markerRLEisRight) // We can be the end when right aligned
     555              :                             ) {
     556            2 :                             bool storedMarkerIsRight = toMarker((*insertionIt)->element)->isRightMarker();
     557            2 :                             markerShouldGoBefore = storedMarkerIsRight;
     558              :                         }
     559           52 :                         if (markerShouldGoBefore
     560           52 :                             || ((*insertionIt)->repeatListElementType == RepeatListElementType::JUMP)
     561              :                             ) {
     562              :                             // Decrease position
     563              :                             // This should always be possible as the list always starts with a REPEAT_START element
     564            2 :                             assert(insertionIt != sectionRLElements.begin());
     565            2 :                             --insertionIt;
     566              :                         } else {
     567              :                             // Found location after which we should go
     568           50 :                             break;
     569              :                         }
     570              :                     }
     571              :                     // We should be inserted right after insertionIt
     572          273 :                     sectionRLElements.insert(insertionIt + 1, markerRLE);
     573              :                 }
     574              :             }
     575              :             // End
     576        14398 :             if (m->repeatEnd()) {
     577          297 :                 sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::REPEAT_END, m, m));
     578          297 :                 if (startFromRepeatMeasure != nullptr) {
     579          297 :                     startFromRepeatMeasure->addToRepeatCount(m->repeatCount() - 1);
     580              :                 }
     581          297 :                 if (volta != nullptr) {
     582              :                     //if (volta->endMeasure()->tick() < m->tick()) {
     583              :                     // The previous volta was supposed to end before us (open volta case) -> insert the end
     584          120 :                     sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::VOLTA_END, volta, m));
     585          120 :                     volta = nullptr;
     586              :                     //} else {
     587              :                     //    // Volta is spanning over this end repeat, consider splitting the volta
     588              :                     //    // or ignoring this repeat all together
     589              :                     //}
     590              :                 }
     591              :             }
     592              :             // Volta end
     593        28796 :             if ((volta != nullptr)
     594          218 :                 && (volta->endMeasure()->tick() == m->tick())
     595        14616 :                 && (volta->getProperty(Pid::END_HOOK_TYPE).value<HookType>() != HookType::NONE)) {
     596              :                 // end of closed volta
     597           38 :                 sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::VOLTA_END, volta, m));
     598           38 :                 volta = nullptr;
     599              :             }
     600              :         }
     601              :         // Section break (or end of score)
     602        14470 :         if (mb->sectionBreak() || !mb->nextMeasureMM()) {
     603         1789 :             if (sectionEndMeasureBase != nullptr) {
     604         1789 :                 if (volta != nullptr) {
     605              :                     //if (volta->endMeasure()->tick() < mb->tick()) {
     606              :                     // The previous volta was supposed to end before us (open volta case) -> insert the end
     607           42 :                     sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::VOLTA_END, volta,
     608           42 :                                                                       toMeasure(sectionEndMeasureBase)));
     609           42 :                     volta = nullptr;
     610              :                     //} else {
     611              :                     //    // Volta is spanning over this section break, consider splitting the volta
     612              :                     //    // and adding it again at the start of the next section
     613              :                     //}
     614              :                 }
     615         1789 :                 sectionRLElements.push_back(new RepeatListElement(RepeatListElementType::SECTION_BREAK, mb,
     616         1789 :                                                                   toMeasure(sectionEndMeasureBase)));
     617         1789 :                 sectionEndMeasureBase = nullptr; // reset to indicate not having found the end for the next section
     618              :                 // store section
     619         1789 :                 m_rlElements.push_back(sectionRLElements);
     620              :             }
     621              :             // prepare for new section
     622         1789 :             if (Measure* nextMeas = mb->nextMeasureMM()) {
     623           30 :                 sectionRLElements = RepeatListElementList();
     624              :                 // First measure of a section/score is always used as a reference REPEAT_START point
     625              :                 // even if it doesn't have a start repeat
     626              :                 startFromRepeatMeasure
     627           30 :                     = new RepeatListElement(RepeatListElementType::REPEAT_START, nextMeas, toMeasure(nextMeas));
     628           30 :                 sectionRLElements.push_back(startFromRepeatMeasure);
     629              :             } else {
     630              :                 // no more measures -> done
     631         1759 :                 break;
     632              :             }
     633              :         }
     634              :     }
     635         1759 : }
     636              : 
     637              : ///
     638              : /// \brief Finds a matching marker in the score (section first)
     639              : /// \remark Special case labels:
     640              : ///         "start" will result in start of current section
     641              : ///         "end" will result in end of current section
     642              : ///
     643          453 : std::pair<std::vector<RepeatListElementList>::const_iterator, RepeatListElementList::const_iterator> RepeatList::findMarker(
     644              :     muse::String label, std::vector<RepeatListElementList>::const_iterator referenceSectionIt,
     645              :     RepeatListElementList::const_iterator referenceRepeatListElementIt) const
     646              : {
     647          453 :     bool found = false;
     648          453 :     std::vector<RepeatListElementList>::const_iterator foundSectionIt;
     649          453 :     RepeatListElementList::const_iterator foundRepeatListElementIt;
     650              : 
     651              :     // Start in the current section
     652          453 :     foundSectionIt = referenceSectionIt;
     653              :     // Handle special label casing first
     654          453 :     if (label == "start") {
     655           77 :         foundRepeatListElementIt = referenceSectionIt->cbegin();
     656           77 :         found = true;
     657          376 :     } else if (label == "end") {
     658           44 :         foundRepeatListElementIt = referenceSectionIt->cend() - 1;
     659           44 :         found = true;
     660              :     } else {
     661          332 :         foundRepeatListElementIt = referenceRepeatListElementIt;
     662              :     }
     663              : 
     664              :     // Search backwards in this section
     665         1609 :     while (!found && (foundRepeatListElementIt != referenceSectionIt->cbegin())) {
     666         1156 :         --foundRepeatListElementIt;
     667         3468 :         if (((*foundRepeatListElementIt)->repeatListElementType == RepeatListElementType::MARKER)
     668         1156 :             && ((toMarker((*foundRepeatListElementIt)->element))->label() == label)
     669              :             ) {
     670          165 :             found = true;
     671              :         }
     672              :     }
     673              : 
     674              :     // Search forwards in this section
     675          453 :     if (!found) {
     676          167 :         foundRepeatListElementIt = referenceRepeatListElementIt + 1;
     677          553 :         while (!found && (foundRepeatListElementIt != referenceSectionIt->cend())) {
     678         1401 :             if (((*foundRepeatListElementIt)->repeatListElementType == RepeatListElementType::MARKER)
     679          467 :                 && ((toMarker((*foundRepeatListElementIt)->element))->label() == label)
     680              :                 ) {
     681           81 :                 found = true;
     682           81 :                 break;
     683              :             }
     684          386 :             ++foundRepeatListElementIt;
     685              :         }
     686              :     }
     687              : 
     688              :     // Search backwards through all previous sections
     689          453 :     if (!found) {
     690           99 :         while (!found && (foundSectionIt != m_rlElements.cbegin())) {
     691           13 :             --foundSectionIt;
     692           13 :             foundRepeatListElementIt = foundSectionIt->cend();
     693           64 :             while (!found && (foundRepeatListElementIt != foundSectionIt->cbegin())) {
     694           51 :                 --foundRepeatListElementIt;
     695          153 :                 if (((*foundRepeatListElementIt)->repeatListElementType == RepeatListElementType::MARKER)
     696           51 :                     && ((toMarker((*foundRepeatListElementIt)->element))->label() == label)
     697              :                     ) {
     698            0 :                     found = true;
     699              :                 }
     700              :             }
     701              :         }
     702              :     }
     703              : 
     704              :     // Search forwards through all following sections
     705          453 :     if (!found) {
     706           86 :         foundSectionIt = referenceSectionIt + 1;
     707          105 :         while (foundSectionIt != m_rlElements.cend()) {
     708           19 :             foundRepeatListElementIt = foundSectionIt->cbegin();
     709          102 :             while (!found && (foundRepeatListElementIt != foundSectionIt->cend())) {
     710          249 :                 if (((*foundRepeatListElementIt)->repeatListElementType == RepeatListElementType::MARKER)
     711           83 :                     && ((toMarker((*foundRepeatListElementIt)->element))->label() == label)
     712              :                     ) {
     713            0 :                     found = true;
     714            0 :                     break;
     715              :                 }
     716           83 :                 ++foundRepeatListElementIt;
     717              :             }
     718           19 :             if (found) {
     719            0 :                 break;
     720              :             }
     721           19 :             ++foundSectionIt;
     722              :         }
     723              :     }
     724              : 
     725          906 :     return std::make_pair(foundSectionIt, foundRepeatListElementIt);
     726              : }
     727              : 
     728              : ///
     729              : /// \brief RepeatList::performJump
     730              : /// \param sectionIt                 [in]   Section of the jump target
     731              : /// \param repeatListElementTargetIt [in]   RepeatListElement of the jump target within the section
     732              : /// \param withRepeats               [in]   Whether first or last playthrough of the target is the actual target, influences playbackCount
     733              : /// \param playbackCount             [out]  Will contain the resulting playbackCount value
     734              : /// \param activeVolta               [out]  Contains a reference to the active Volta for jump target
     735              : /// \param startRepeatReference      [out]  Reference point to return to and compare against for jump target
     736              : ///
     737          215 : void RepeatList::performJump(std::vector<RepeatListElementList>::const_iterator sectionIt,
     738              :                              RepeatListElementList::const_iterator repeatListElementTargetIt,
     739              :                              bool withRepeats, int* const playbackCount,
     740              :                              Volta const** const activeVolta, RepeatListElement const** const startRepeatReference) const
     741              : {
     742          215 :     RepeatListElementList::const_iterator repeatListElementIt;
     743              :     // Fast forward processing up to our desired marker
     744          215 :     *activeVolta = nullptr;
     745          215 :     *startRepeatReference = *(sectionIt->cbegin());
     746          816 :     for (repeatListElementIt = sectionIt->cbegin(); repeatListElementIt != repeatListElementTargetIt; ++repeatListElementIt) {
     747          601 :         switch ((*repeatListElementIt)->repeatListElementType) {
     748           66 :         case RepeatListElementType::VOLTA_START: {
     749           66 :             *activeVolta = toVolta((*repeatListElementIt)->element);
     750           66 :         } break;
     751           61 :         case RepeatListElementType::VOLTA_END: {
     752           61 :             *activeVolta = nullptr;
     753           61 :         } break;
     754          182 :         case RepeatListElementType::REPEAT_START: {
     755          182 :             *startRepeatReference = *repeatListElementIt;
     756          182 :         } break;
     757          292 :         default: {
     758              :             // nothing to do
     759          292 :         } break;
     760              :         }
     761              :     }
     762              :     // Set the correct playbackCount
     763          215 :     if (withRepeats) {
     764           88 :         if (*activeVolta != nullptr) {
     765            1 :             *playbackCount = (*activeVolta)->firstEnding();
     766            1 :             if (*playbackCount == 0) {
     767            0 :                 *playbackCount = 1;
     768              :             }
     769              :         } else {
     770           87 :             *playbackCount = 1;
     771              :         }
     772              :     } else { // Final repeat after jump
     773          127 :         if (*activeVolta != nullptr) {
     774            4 :             *playbackCount = (*activeVolta)->lastEnding();
     775            4 :             if (*playbackCount == 0) {
     776            0 :                 *playbackCount = (*startRepeatReference)->getRepeatCount();
     777              :             }
     778              :         } else {
     779          123 :             *playbackCount = (*startRepeatReference)->getRepeatCount();
     780              :         }
     781              :     }
     782          215 : }
     783              : 
     784              : ///
     785              : /// \brief RepeatList::unwind
     786              : ///
     787         1807 : void RepeatList::unwind()
     788              : {
     789         1821 :     TRACEFUNC;
     790              : 
     791         1807 :     muse::DeleteAll(*this);
     792         1807 :     clear();
     793         1807 :     m_jumpsTaken.clear();
     794              : 
     795         1807 :     if (!m_score->firstMeasureMM()) {
     796           48 :         return;
     797              :     }
     798              : 
     799         1759 :     collectRepeatListElements();
     800              : 
     801              :     // Following variables are used during unwinding, but may be altered when following jumps
     802              :     // Therefor they are declared outside of the loop
     803         1759 :     RepeatSegment* rs = nullptr;
     804              :     int playbackCount;
     805         1759 :     Volta const* activeVolta = nullptr;
     806              :     std::pair<std::vector<RepeatListElementList>::const_iterator, RepeatListElementList::const_iterator> playUntil
     807         1759 :         = std::make_pair(m_rlElements.cend(), m_rlElements[0].cend());
     808              :     std::pair<std::vector<RepeatListElementList>::const_iterator, RepeatListElementList::const_iterator> continueAt
     809         1759 :         = std::make_pair(m_rlElements.cend(), m_rlElements[0].cend());
     810         1759 :     Jump const* activeJump = nullptr;
     811         1759 :     bool forceFinalRepeat = false;   // Used during jump processing
     812         1759 :     RepeatListElementList::const_iterator repeatListElementIt;
     813              : 
     814         3548 :     for (std::vector<RepeatListElementList>::const_iterator sectionIt = m_rlElements.cbegin(); sectionIt != m_rlElements.cend();
     815         1789 :          ++sectionIt) {
     816              :         // Unwind this section
     817              :         RepeatListElement const* startRepeatReference;
     818         1789 :         playbackCount = 1;
     819         1789 :         repeatListElementIt = sectionIt->cbegin();     // Should always be a REPEAT_START indicator
     820         1789 :         startRepeatReference = *repeatListElementIt;
     821         1789 :         activeVolta = nullptr;
     822         1789 :         playUntil.first = m_rlElements.cend();
     823         1789 :         continueAt.first = m_rlElements.cend();
     824         1789 :         forceFinalRepeat = false;
     825              : 
     826         1789 :         rs = new RepeatSegment(playbackCount);
     827         1789 :         rs->addMeasure((*repeatListElementIt)->measure);
     828         1789 :         ++repeatListElementIt;
     829              : 
     830         5858 :         while (repeatListElementIt != sectionIt->cend()) {
     831         4069 :             if (rs) {
     832         3697 :                 rs->addMeasures((*repeatListElementIt)->measure);
     833              :             }
     834         4069 :             switch ((*repeatListElementIt)->repeatListElementType) {
     835         1764 :             case RepeatListElementType::SECTION_BREAK: {
     836         1764 :                 if ((rs != nullptr) && (!rs->isEmpty())) {
     837         1755 :                     push_back(rs);
     838              :                 }
     839         1764 :             } break;
     840          458 :             case RepeatListElementType::VOLTA_START: {
     841          458 :                 activeVolta = toVolta((*repeatListElementIt)->element);
     842          458 :                 if (!(activeVolta->hasEnding(playbackCount))) {
     843              :                     // Should be skipped, remove our measure from rs
     844          202 :                     rs->popMeasure();
     845          202 :                     if (!rs->isEmpty()) {
     846          146 :                         push_back(rs);
     847              :                     }
     848              :                     // Skip the volta
     849              :                     do {
     850          372 :                         ++repeatListElementIt;
     851          372 :                     } while ((*repeatListElementIt)->repeatListElementType != RepeatListElementType::VOLTA_END);
     852          202 :                     activeVolta = nullptr;
     853              :                     // Start next rs on the following measure
     854          202 :                     Measure const* const possibleNextMeasure = (*repeatListElementIt)->measure->nextMeasureMM();
     855          202 :                     if (possibleNextMeasure == nullptr) {
     856            9 :                         rs = nullptr;                   // end of score, but will still encounter section break, notify it
     857              :                     } else {
     858          193 :                         rs = new RepeatSegment(playbackCount);
     859          193 :                         rs->addMeasure(possibleNextMeasure);
     860              :                     }
     861              :                 }
     862              :                 // else { take the volta, it's already set as activeVolta, so just continue }
     863          458 :             } break;
     864          107 :             case RepeatListElementType::VOLTA_END: {
     865          107 :                 activeVolta = nullptr;
     866          107 :             } break;
     867          562 :             case RepeatListElementType::REPEAT_START: {
     868          562 :                 if (rs == nullptr) {               // Sent here by an end-repeat
     869          363 :                     rs = new RepeatSegment(playbackCount);
     870          363 :                     rs->addMeasure((*repeatListElementIt)->measure);
     871              :                 } else {
     872          199 :                     int desiredPlaybackCount = (forceFinalRepeat) ? (*repeatListElementIt)->getRepeatCount() : 1;
     873          199 :                     if (rs->playbackCount != desiredPlaybackCount) {
     874              :                         // Restart a segment as we have a new playbackCount reference
     875           56 :                         rs->popMeasure();
     876           56 :                         if (!rs->isEmpty()) {
     877           54 :                             push_back(rs);
     878              :                         }
     879           56 :                         playbackCount = desiredPlaybackCount;
     880           56 :                         rs = new RepeatSegment(playbackCount);
     881           56 :                         rs->addMeasure((*repeatListElementIt)->measure);
     882              :                     }
     883          199 :                     startRepeatReference = *repeatListElementIt;
     884              :                 }
     885          562 :             } break;
     886          572 :             case RepeatListElementType::REPEAT_END: {
     887          572 :                 (*repeatListElementIt)->addToRepeatCount(1);
     888          572 :                 if ((playbackCount < startRepeatReference->getRepeatCount())
     889          572 :                     && ((*repeatListElementIt)->getRepeatCount() < (*repeatListElementIt)->measure->repeatCount())
     890              :                     ) {
     891              :                     // Honor the repeat
     892          363 :                     if ((rs != nullptr) && (!rs->isEmpty())) {
     893          363 :                         push_back(rs);
     894              :                     }
     895          363 :                     rs = nullptr;
     896              :                     do {                 // rewind
     897          811 :                         --repeatListElementIt;
     898          811 :                         if ((*repeatListElementIt)->repeatListElementType == RepeatListElementType::VOLTA_START) {
     899          230 :                             activeVolta = nullptr;
     900          581 :                         } else if ((*repeatListElementIt)->repeatListElementType == RepeatListElementType::VOLTA_END) {
     901          101 :                             activeVolta = toVolta((*repeatListElementIt)->element);
     902              :                         }
     903          811 :                     } while ((*repeatListElementIt) != startRepeatReference);
     904          363 :                     ++playbackCount;
     905          363 :                     continue;                 // Force evaluation of the start repeat
     906              :                 }
     907              :                 //else { // Exhausted repeats, just continue }
     908          209 :             } break;
     909          229 :             case RepeatListElementType::JUMP: {
     910          229 :                 if (activeJump == toJump((*repeatListElementIt)->element)) {
     911              :                     // We've reached this jump again after just having taken it
     912              :                     // If this jump forced final repeat playthrough of measures
     913              :                     // it only forces so up to this point - see test::repeat23
     914           57 :                     forceFinalRepeat = false;
     915              :                 }
     916              :                 // TODO future improvement, use the repeatList property from the Jump to verify if it should be honored
     917              :                 // For now (or in case of an empty repeatList property) a Jump is only honored on final playthrough
     918              :                 // of the measure, and only taken once
     919          229 :                 if ((playbackCount >= startRepeatReference->getRepeatCount())
     920          229 :                     || ((activeVolta != nullptr) && (playbackCount == activeVolta->lastEnding()))
     921              :                     ) {
     922              :                     std::pair<Jump const* const,
     923          213 :                               int> jumpOccurrence = std::make_pair(toJump((*repeatListElementIt)->element), playbackCount);
     924          213 :                     if (m_jumpsTaken.find(jumpOccurrence) == m_jumpsTaken.end()) {                 // Not yet processed
     925              :                         // Processing it now
     926          151 :                         m_jumpsTaken.insert(jumpOccurrence);
     927              :                         // Find the jump targets
     928              :                         std::pair<std::vector<RepeatListElementList>::const_iterator,
     929          151 :                                   RepeatListElementList::const_iterator> jumpTo = findMarker(
     930          302 :                             jumpOccurrence.first->jumpTo(), sectionIt, repeatListElementIt);
     931          151 :                         playUntil = findMarker(jumpOccurrence.first->playUntil(), sectionIt, repeatListElementIt);
     932          151 :                         continueAt = findMarker(jumpOccurrence.first->continueAt(), sectionIt, repeatListElementIt);
     933              : 
     934              :                         // Execute
     935          151 :                         if (jumpTo.first != m_rlElements.cend()) {
     936          147 :                             if (rs && !rs->isEmpty()) {
     937          147 :                                 push_back(rs);
     938              :                             }
     939          147 :                             rs = nullptr;
     940              : 
     941          147 :                             activeJump = jumpOccurrence.first;
     942          147 :                             performJump(jumpTo.first, jumpTo.second,
     943          147 :                                         activeJump->playRepeats(), &playbackCount, &activeVolta, &startRepeatReference);
     944          147 :                             sectionIt = jumpTo.first;
     945          147 :                             repeatListElementIt = jumpTo.second;
     946          147 :                             forceFinalRepeat = !(activeJump->playRepeats());
     947              : 
     948              :                             // Re-evaluate our repeat count for end repeat measures
     949          147 :                             if (playUntil.first != m_rlElements.cend()) {                     // Only required if we have an end target
     950         1108 :                                 for (auto rleIt = repeatListElementIt + 1; rleIt != sectionIt->cend(); ++rleIt) {
     951          969 :                                     if (((*rleIt)->repeatListElementType == RepeatListElementType::REPEAT_END)
     952          969 :                                         && ((*rleIt)->getRepeatCount() != 0)
     953              :                                         ) {                        // We've been played before - see test::repeat22 for why only then
     954              :                                         // Clear out our current repeat count
     955           75 :                                         (*rleIt)->addToRepeatCount(-((*rleIt)->getRepeatCount()));
     956           75 :                                         if (forceFinalRepeat) {
     957              :                                             // Set repeat count to the maximum number for this repeat
     958           57 :                                             (*rleIt)->addToRepeatCount((*rleIt)->measure->repeatCount());
     959              :                                         }
     960              :                                     }
     961              :                                 }
     962              :                             }
     963          147 :                             if (activeVolta != nullptr) {
     964              :                                 // We jumped into a volta, which might not be the only one of this startRepeatReference
     965              :                                 // If our jump didn't take us to the last playthrough, we must re-evaluate
     966              :                                 // all voltas related to this startRepeatReference
     967            5 :                                 if (playbackCount < startRepeatReference->getRepeatCount()) {
     968              :                                     // test::repeat52 and test::repeat53
     969            3 :                                     RepeatListElementList::const_iterator findRepeatIt = repeatListElementIt;
     970              :                                     do {
     971            9 :                                         --findRepeatIt;
     972            9 :                                     } while ((*findRepeatIt) != startRepeatReference);
     973            3 :                                     ++findRepeatIt;                         // Start volta analysis past this start repeat
     974            3 :                                     Volta const* voltaReference = nullptr;
     975            3 :                                     int processedRepeatCount = 1;
     976           18 :                                     while ((findRepeatIt != sectionIt->cend())
     977           18 :                                            && ((*findRepeatIt)->repeatListElementType != RepeatListElementType::REPEAT_START)
     978           36 :                                            && (processedRepeatCount < startRepeatReference->getRepeatCount())
     979              :                                            ) {
     980           15 :                                         if ((*findRepeatIt)->repeatListElementType == RepeatListElementType::VOLTA_START) {
     981            5 :                                             voltaReference = toVolta((*findRepeatIt)->element);
     982            5 :                                             if (voltaReference->lastEnding() < playbackCount) {
     983              :                                                 // We won't pass here anymore - no need to adjust end repeat to this volta
     984            0 :                                                 voltaReference = nullptr;
     985              :                                             }
     986           10 :                                         } else if ((*findRepeatIt)->repeatListElementType == RepeatListElementType::REPEAT_END) {
     987            5 :                                             if (voltaReference != nullptr) {
     988            5 :                                                 int remainingRepeatsCount = 0;
     989           14 :                                                 for (int ending : voltaReference->endings()) {
     990            9 :                                                     if (ending >= playbackCount) {
     991            6 :                                                         ++remainingRepeatsCount;
     992              :                                                     }
     993            5 :                                                 }
     994              :                                                 // Apply new remainingRepeatsCount
     995            5 :                                                 (*findRepeatIt)->addToRepeatCount(-((*findRepeatIt)->getRepeatCount()));
     996            5 :                                                 (*findRepeatIt)->addToRepeatCount(
     997            5 :                                                     (*findRepeatIt)->measure->repeatCount() - remainingRepeatsCount - 1);
     998              :                                             }
     999            5 :                                             processedRepeatCount += (*findRepeatIt)->measure->repeatCount() - 1;
    1000              :                                         }
    1001           15 :                                         ++findRepeatIt;
    1002              :                                     }
    1003              :                                 }
    1004              :                             }
    1005              : 
    1006              :                             // We've arrived at the target marker
    1007          147 :                             rs = new RepeatSegment(playbackCount);
    1008          147 :                             rs->addMeasure((*repeatListElementIt)->measure);
    1009              :                         }                         // End of jump execution
    1010              :                     }
    1011              :                 }
    1012          229 :             } break;
    1013          377 :             case RepeatListElementType::MARKER: {
    1014          524 :                 if (((sectionIt == playUntil.first) && (repeatListElementIt == playUntil.second))
    1015          525 :                     && ((playbackCount == startRepeatReference->getRepeatCount())
    1016            1 :                         || ((activeVolta != nullptr) && (playbackCount == activeVolta->lastEnding()))
    1017              :                         )
    1018              :                     ) {
    1019              :                     // Found final playThrough of this Marker
    1020           93 :                     if (rs && !rs->isEmpty()) {
    1021           93 :                         push_back(rs);
    1022              :                     }
    1023           93 :                     rs = nullptr;
    1024           93 :                     playUntil.first = m_rlElements.cend();                 // Clear this reference - processed
    1025           93 :                     forceFinalRepeat = false;
    1026              : 
    1027           93 :                     if (continueAt.first != m_rlElements.cend()) {
    1028           68 :                         performJump(continueAt.first, continueAt.second, true, &playbackCount, &activeVolta, &startRepeatReference);
    1029           68 :                         sectionIt = continueAt.first;
    1030           68 :                         repeatListElementIt = continueAt.second;
    1031           68 :                         if (startRepeatReference->measure != (*(continueAt.second))->measure) {
    1032              :                             // Use the continueAt target as repeat reference if it wasn't this measure already
    1033              :                             // Check for measure is required as REPEAT_START is processed before a marker
    1034              :                             // see test::repeat23 m12
    1035           67 :                             startRepeatReference = *(continueAt.second);
    1036              :                         }
    1037              : 
    1038              :                         // We've arrived at the target marker
    1039           68 :                         rs = new RepeatSegment(playbackCount);
    1040           68 :                         rs->addMeasure((*repeatListElementIt)->measure);
    1041              : 
    1042           68 :                         continueAt.first = m_rlElements.cend();                   // Clear this reference - processed
    1043              :                     } else { // Nowhere to go to, break out of this section loop and onto the next section
    1044           25 :                         repeatListElementIt = sectionIt->cend();
    1045           25 :                         continue;
    1046              :                     }
    1047              :                 }
    1048          352 :             } break;
    1049              :             }
    1050         3681 :             ++repeatListElementIt;
    1051              :         }
    1052              : 
    1053              :         // Reached the end of this section
    1054         1789 :         if (!this->empty()) {
    1055              :             // Inform the last RepeatSegment that the Section Break pause property should be honored now
    1056         1788 :             rs = this->back();
    1057         1788 :             repeatListElementIt = sectionIt->cend() - 1;
    1058         1788 :             assert((*repeatListElementIt)->repeatListElementType == RepeatListElementType::SECTION_BREAK);
    1059              : 
    1060         1788 :             LayoutBreak const* const layoutBreak = toMeasureBase((*repeatListElementIt)->element)->sectionBreakElement();
    1061         1788 :             if (layoutBreak != nullptr) {
    1062           31 :                 rs->pause = layoutBreak->pause();
    1063              :             }
    1064              :         }
    1065              :     }
    1066              : 
    1067         1759 :     updateUticks();
    1068         1759 :     m_expanded = true;
    1069         1807 : }
    1070              : }
        

Generated by: LCOV version 2.0-1