LCOV - code coverage report
Current view: top level - src/engraving/playback/utils - repeatutils.h Coverage Total Hit
Test: filtered_coverage.info Lines: 84.5 % 58 49
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) 2025 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              : #pragma once
      24              : 
      25              : #include "dom/measure.h"
      26              : #include "dom/note.h"
      27              : #include "dom/repeatlist.h"
      28              : 
      29              : #include "playback/renderingcontext.h"
      30              : 
      31              : namespace mu::engraving {
      32              : struct PartiallyTiedNoteInfo {
      33              :     const RepeatSegment* repeat = nullptr;
      34              :     const Note* note = nullptr;
      35              : 
      36           36 :     bool isValid() const { return repeat && note; }
      37              : };
      38              : 
      39           27 : inline PartiallyTiedNoteInfo findOutgoingNoteInPreviousRepeat(const Note* incomingNote, const RenderingContext& ctx)
      40              : {
      41           27 :     const RepeatList& repeats = ctx.score->repeatList();
      42           27 :     auto currRepeatIt = repeats.findRepeatSegmentFromUTick(ctx.nominalPositionStartTick + ctx.positionTickOffset);
      43           27 :     if (currRepeatIt == repeats.cbegin() || currRepeatIt == repeats.cend()) {
      44           12 :         return PartiallyTiedNoteInfo();
      45              :     }
      46              : 
      47           15 :     if (ctx.nominalPositionStartTick + ctx.positionTickOffset != (*currRepeatIt)->utick) {
      48            0 :         return PartiallyTiedNoteInfo(); // the incoming note is not the first note in this repeat
      49              :     }
      50              : 
      51           15 :     auto prevRepeatIt = std::prev(currRepeatIt);
      52           15 :     const RepeatSegment* prevRepeat = (*prevRepeatIt);
      53           15 :     const Measure* lastMeasure = prevRepeat->lastMeasure();
      54           15 :     if (!lastMeasure) {
      55            0 :         return PartiallyTiedNoteInfo();
      56              :     }
      57              : 
      58           15 :     const ChordRest* lastChordRest = lastMeasure->lastChordRest(incomingNote->track());
      59           15 :     if (!lastChordRest || !lastChordRest->isChord()) {
      60            0 :         return PartiallyTiedNoteInfo();
      61              :     }
      62              : 
      63           15 :     const Note* outgoingNote = toChord(lastChordRest)->findNote(incomingNote->pitch());
      64           15 :     const Tie* tie = outgoingNote ? outgoingNote->tieFor() : nullptr;
      65           15 :     if (tie && tie->playSpanner()) {
      66           15 :         return { prevRepeat, outgoingNote };
      67              :     }
      68              : 
      69            0 :     return PartiallyTiedNoteInfo();
      70              : }
      71              : 
      72            9 : inline PartiallyTiedNoteInfo findIncomingNoteInNextRepeat(const Note* outgoingNote, const RenderingContext& ctx)
      73              : {
      74            9 :     const RepeatList& repeats = ctx.score->repeatList();
      75            9 :     auto currRepeatIt = repeats.findRepeatSegmentFromUTick(ctx.nominalPositionStartTick + ctx.positionTickOffset);
      76            9 :     if (currRepeatIt == repeats.cend()) {
      77            0 :         return PartiallyTiedNoteInfo();
      78              :     }
      79              : 
      80            9 :     const RepeatSegment* currRepeat = (*currRepeatIt);
      81            9 :     const int currRepeatLastTick = currRepeat->utick + currRepeat->len();
      82            9 :     if (ctx.nominalPositionStartTick + ctx.positionTickOffset != currRepeatLastTick - ctx.nominalDurationTicks) {
      83            1 :         return PartiallyTiedNoteInfo(); // the outgoing note is not the last note in this repeat
      84              :     }
      85              : 
      86            8 :     auto nextRepeatIt = std::next(currRepeatIt);
      87            8 :     if (nextRepeatIt == repeats.cend()) {
      88            2 :         return PartiallyTiedNoteInfo();
      89              :     }
      90              : 
      91            6 :     const RepeatSegment* nextRepeat = (*nextRepeatIt);
      92            6 :     const Measure* firstMeasure = nextRepeat->firstMeasure();
      93            6 :     if (!firstMeasure) {
      94            0 :         return PartiallyTiedNoteInfo();
      95              :     }
      96              : 
      97            6 :     const ChordRest* firstChordRest = firstMeasure->firstChordRest(outgoingNote->track());
      98            6 :     if (!firstChordRest || !firstChordRest->isChord()) {
      99            0 :         return PartiallyTiedNoteInfo();
     100              :     }
     101              : 
     102            6 :     const Note* incomingNote = toChord(firstChordRest)->findNote(outgoingNote->pitch());
     103            6 :     const Tie* tie = incomingNote ? incomingNote->tieBack() : nullptr;
     104            6 :     if (tie && tie->playSpanner()) {
     105            6 :         return { nextRepeat, incomingNote };
     106              :     }
     107              : 
     108            0 :     return PartiallyTiedNoteInfo();
     109              : }
     110              : 
     111           38 : inline bool notesInSameRepeat(const Score* score, const Note* note1, const Note* note2, const int tickPositionOffset)
     112              : {
     113           38 :     const RepeatList& repeats = score->repeatList();
     114           38 :     if (repeats.size() == 1) {
     115           23 :         return true;
     116              :     }
     117              : 
     118           15 :     const int firstNoteTick = note1->tick().ticks();
     119           15 :     auto repeatIt = repeats.findRepeatSegmentFromUTick(firstNoteTick + tickPositionOffset);
     120           15 :     if (repeatIt == repeats.end()) {
     121            0 :         return true;
     122              :     }
     123              : 
     124           15 :     const RepeatSegment* repeat = (*repeatIt);
     125           15 :     const int secondNoteTick = note2->tick().ticks();
     126              : 
     127           15 :     if (repeat->tick > secondNoteTick || repeat->endTick() <= secondNoteTick) {
     128            4 :         return false;
     129              :     }
     130              : 
     131           11 :     return true;
     132              : }
     133              : }
        

Generated by: LCOV version 2.0-1