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 : #include "readinglist.h"
24 :
25 : namespace mu::iex::tabledit {
26 13 : bool MeasureStatus::isRegular()
27 : {
28 13 : if (barlineDouble || barlineEnd || repeatStart || repeatEnd) {
29 6 : return false;
30 : }
31 :
32 7 : return true;
33 : }
34 :
35 : // return the number of exact matches at the front of the list
36 : // due to compaction, this is rare
37 :
38 18 : size_t findExactMatches(const std::vector<SimplifiedItem>& list)
39 : {
40 18 : if (list.size() == 0) {
41 0 : return 0; // shouldn't happen
42 : }
43 18 : size_t res { 1 };
44 25 : while (res < list.size() && list.at(0) == list.at(res)) {
45 7 : ++res;
46 : }
47 18 : return res;
48 : }
49 :
50 : // return the number of exact matches at the second items on the list
51 :
52 2 : size_t findExactMatchesFrom2nd(const std::vector<SimplifiedItem>& list)
53 : {
54 2 : if (list.size() == 0) {
55 0 : return 0; // shouldn't happen
56 : }
57 2 : size_t res { 1 };
58 2 : while (res + 1 < list.size() && list.at(1) == list.at(res + 1)) {
59 0 : ++res;
60 : }
61 2 : return res;
62 : }
63 :
64 : // copy all valid TefReadingListItems to m_list
65 : // with the measure numbers changed from one-based to zero-based
66 : // and items describing consecutive sets of measures merged
67 :
68 61 : void ReadingList::initializeList(const size_t nMeasures, const std::vector<TefReadingListItem>& tefReadingList)
69 : {
70 : UNUSED(nMeasures);
71 92 : for (const auto& tefItem : tefReadingList) {
72 : if (/* todo: item is valid */ true) {
73 31 : SimplifiedItem i;
74 31 : i.first = tefItem.firstMeasure - 1;
75 31 : i.last = tefItem.lastMeasure - 1;
76 31 : if (m_list.empty()) {
77 : // just add
78 13 : m_list.push_back(i);
79 18 : } else if (m_list.back().isConsecutiveWith(i)) {
80 2 : m_list.back() += i;
81 : } else {
82 : // just add
83 16 : m_list.push_back(i);
84 : }
85 : }
86 : LOGN("items");
87 86 : for (auto i : m_list) {
88 : LOGN("first %d last %d", i.first + 1, i.last + 1);
89 : }
90 : }
91 61 : }
92 :
93 61 : void ReadingList::calculate(const size_t nMeasures, const std::vector<TefReadingListItem>& tefReadingList)
94 : {
95 : LOGN("reading list size %zu number of measures %zu", tefReadingList.size(), nMeasures);
96 61 : if (nMeasures == 0) {
97 0 : return;
98 : }
99 61 : initializeList(nMeasures, tefReadingList);
100 61 : m_status.resize(nMeasures);
101 202 : for (const auto& stat : m_status) {
102 : LOGN("1 repeatEnd %d repeatStart %d", stat.repeatEnd, stat.repeatStart);
103 : }
104 61 : if (!m_list.empty()) {
105 13 : analyze();
106 : }
107 202 : for (const auto& stat : m_status) {
108 : LOGN("2 repeatEnd %d repeatStart %d", stat.repeatEnd, stat.repeatStart);
109 : }
110 : }
111 :
112 13 : void ReadingList::analyze()
113 : {
114 : // todo: check handling single remaining segment
115 31 : while (m_list.size()) {
116 18 : size_t itemsUsed { findExactMatches(m_list) };
117 : LOGN("findExactMatches() count %zu", itemsUsed);
118 18 : bool overlapWithNext { m_list.size() > itemsUsed && m_list.at(0).overlapsWith(m_list.at(itemsUsed)) };
119 : LOGN("overlapWithNext %s", (overlapWithNext ? "true" : "false"));
120 18 : if (itemsUsed > 1) {
121 4 : m_status[m_list.at(0).first].repeatStart = true;
122 4 : m_status[m_list.at(itemsUsed - 1).last].repeatEnd = true;
123 4 : if (itemsUsed > 2) {
124 2 : m_status[m_list.at(itemsUsed - 1).last].repeatCount = static_cast<int>(itemsUsed);
125 : }
126 4 : m_list.erase(m_list.begin(), m_list.begin() + itemsUsed);
127 4 : continue;
128 : }
129 :
130 14 : if (m_list.size() >= 2 && m_list.at(0).overlapsWith(m_list.at(1))) {
131 : LOGN("overlap %d-%d %d-%d", m_list.at(0).first, m_list.at(0).last, m_list.at(1).first, m_list.at(1).last);
132 7 : if (m_list.at(0).first <= m_list.at(1).first) {
133 7 : m_status[m_list.at(1).first].repeatStart = true;
134 : } else {
135 : // TableEdit:
136 : // starts playing at list.at(1).first
137 : // repeats from list.at(0).first
138 : // but does not show where to start playing
139 : // MuseScore: tbd
140 : // test file 010.tef
141 : LOGN("overlap: second starts before first");
142 0 : m_status[m_list.at(1).first].repeatStart = true;
143 : }
144 7 : if (m_list.at(0).last < m_list.at(1).last) {
145 3 : m_status[m_list.at(0).last].repeatEnd = true;
146 3 : itemsUsed = 1;
147 4 : } else if (m_list.at(0).last == m_list.at(1).last) {
148 2 : m_status[m_list.at(0).last].repeatEnd = true;
149 2 : itemsUsed = 2;
150 : } else {
151 : // todo: how to handle at(0) not consecutive with at(2) ?
152 : // todo: handle multiple repeats of first ending (alternative_ending_2a, 4b and 4d)
153 : // -> first measure in ending 1 gets count = #repeats - 1
154 : // todo: handle multiple repeats of second ending (alternative_ending_2b and 4e)
155 : // -> first measure in ending 2 gets count = #repeats - 1
156 : LOGN("overlap: second ends before first");
157 2 : if (m_list.size() > 2 && m_list.at(0).isConsecutiveWith(m_list.at(2))) {
158 : // alternative ending
159 2 : m_status[m_list.at(0).last].repeatEnd = true;
160 2 : Ending ending2;
161 2 : ending2.number = 2;
162 2 : m_status[m_list.at(2).first].ending = ending2;
163 2 : const int measuresInEnding1 { m_list.at(0).last - m_list.at(1).last };
164 2 : const size_t ending1Repeats { findExactMatchesFrom2nd(m_list) };
165 : LOGN("ending 1 end %d measures %d repeats %zu", m_list.at(0).last, measuresInEnding1, ending1Repeats);
166 2 : Ending ending1;
167 2 : ending1.duration = measuresInEnding1;
168 2 : ending1.number = 1;
169 2 : m_status[m_list.at(0).last - measuresInEnding1 + 1].ending = ending1;
170 : }
171 2 : itemsUsed = 2;
172 : }
173 7 : m_list.erase(m_list.begin(), m_list.begin() + itemsUsed);
174 7 : continue;
175 7 : }
176 :
177 7 : itemsUsed = 1;
178 7 : m_list.erase(m_list.begin(), m_list.begin() + itemsUsed);
179 7 : continue;
180 7 : }
181 13 : if (m_status.back().isRegular()) {
182 7 : m_status.back().barlineEnd = true;
183 : }
184 13 : }
185 : } // namespace mu::iex::tabledit
|