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 "groups.h"
24 :
25 : #include <unordered_map>
26 :
27 : #include "chordrest.h"
28 : #include "durationtype.h"
29 : #include "measure.h"
30 : #include "staff.h"
31 : #include "timesig.h"
32 : #include "tuplet.h"
33 :
34 : #include "log.h"
35 :
36 : using namespace mu;
37 :
38 : namespace mu::engraving {
39 : //---------------------------------------------------------
40 : // NoteGroup
41 : //---------------------------------------------------------
42 :
43 : struct NoteGroup {
44 : Fraction timeSig;
45 : Groups endings;
46 : };
47 :
48 : //---------------------------------------------------------
49 : // noteGroups
50 : //---------------------------------------------------------
51 :
52 : static std::vector<NoteGroup> noteGroups {
53 : { Fraction(2, 2),
54 : Groups({ { 4, 0x200 }, { 8, 0x110 }, { 12, 0x200 }, { 16, 0x111 }, { 20, 0x200 }, { 24, 0x110 }, { 28, 0x200 } })
55 : },
56 : { Fraction(3, 2),
57 : Groups({ { 4, 0x200 }, { 8, 0x110 }, { 12, 0x200 }, { 16, 0x111 }, { 20, 0x200 }, { 24, 0x110 }, { 28, 0x200 },
58 : { 32, 0x111 }, { 36, 0x200 }, { 40, 0x110 }, { 44, 0x200 } })
59 : },
60 : { Fraction(4, 2),
61 : Groups({ { 4, 0x200 }, { 8, 0x110 }, { 12, 0x200 }, { 16, 0x111 }, { 20, 0x200 }, { 24, 0x110 }, { 28, 0x200 },
62 : { 32, 0x111 }, { 36, 0x200 }, { 40, 0x110 }, { 44, 0x200 }, { 48, 0x111 }, { 52, 0x200 }, { 56, 0x110 }, { 60, 0x200 } })
63 : },
64 : { Fraction(2, 4),
65 : Groups({ { 4, 0x200 }, { 8, 0x111 }, { 12, 0x200 } })
66 : },
67 : { Fraction(3, 4),
68 : Groups({ { 4, 0x200 }, { 8, 0x111 }, { 12, 0x200 }, { 16, 0x111 }, { 20, 0x200 } })
69 : },
70 : { Fraction(4, 4),
71 : Groups({ { 4, 0x200 }, { 8, 0x110 }, { 12, 0x200 }, { 16, 0x111 }, { 20, 0x200 }, { 24, 0x110 }, { 28, 0x200 } })
72 : },
73 : { Fraction(5, 4),
74 : Groups({ { 4, 0x200 }, { 8, 0x110 }, { 12, 0x200 }, { 16, 0x110 }, { 20, 0x200 }, { 24, 0x111 }, { 28, 0x200 },
75 : { 32, 0x110 }, { 36, 0x200 } })
76 : },
77 : { Fraction(6, 4),
78 : Groups({ { 4, 0x200 }, { 8, 0x110 }, { 12, 0x200 }, { 16, 0x110 }, { 20, 0x200 }, { 24, 0x111 }, { 28, 0x200 },
79 : { 32, 0x110 }, { 36, 0x200 }, { 40, 0x110 }, { 44, 0x200 } })
80 : },
81 : { Fraction(3, 8),
82 : Groups({ { 4, 0x200 }, { 8, 0x200 } })
83 : },
84 : { Fraction(5, 8),
85 : Groups({ { 4, 0x200 }, { 8, 0x200 }, { 12, 0x111 }, { 16, 0x200 } })
86 : },
87 : { Fraction(6, 8),
88 : Groups({ { 4, 0x200 }, { 8, 0x200 }, { 12, 0x111 }, { 16, 0x200 }, { 20, 0x200 } })
89 : },
90 : { Fraction(7, 8),
91 : Groups({ { 4, 0x200 }, { 8, 0x200 }, { 12, 0x111 }, { 16, 0x200 }, { 20, 0x111 }, { 24, 0x200 } })
92 : },
93 : { Fraction(9, 8),
94 : Groups({ { 4, 0x200 }, { 8, 0x200 }, { 12, 0x111 }, { 16, 0x200 }, { 20, 0x200 }, { 24, 0x111 }, { 28, 0x200 }, { 32, 0x200 } })
95 : },
96 : { Fraction(12, 8),
97 : Groups({ { 4, 0x200 }, { 8, 0x200 }, { 12, 0x111 }, { 16, 0x200 }, { 20, 0x200 }, { 24, 0x111 }, { 28, 0x200 },
98 : { 32, 0x200 }, { 36, 0x111 }, { 40, 0x200 }, { 44, 0x200 } })
99 : },
100 : };
101 :
102 : //---------------------------------------------------------
103 : // baseBeamMode
104 : // based on time signature properties but respecting manual settings
105 : //---------------------------------------------------------
106 :
107 135470 : BeamMode Groups::baseBeamMode(const ChordRest* cr, const ChordRest* prev)
108 : {
109 135470 : if (cr->isGrace() || cr->beamMode() != BeamMode::AUTO) {
110 60946 : return cr->beamMode();
111 : }
112 74524 : assert(cr->staff());
113 :
114 : // we need to figure out the longest note value beat upon which cr falls in the measure
115 74524 : Fraction maxTickLen = std::max(cr->ticks(), prev ? prev->ticks() : Fraction());
116 74524 : Fraction smallestTickLen = Fraction(1, 8); // start with 8th
117 74524 : Fraction tickLenLimit = Fraction(1, 32); // only check up to 32nds because that's all thats available
118 : // in timesig properties
119 131041 : while (smallestTickLen > maxTickLen || cr->rtick().ticks() % smallestTickLen.ticks() != 0) {
120 77071 : smallestTickLen /= 2; // proceed to 16th, 32nd, etc
121 77071 : if (smallestTickLen < tickLenLimit) {
122 20554 : smallestTickLen = cr->ticks();
123 20554 : break;
124 : }
125 : }
126 74524 : DurationType bigBeatDuration = TDuration(smallestTickLen).type();
127 :
128 74524 : TDuration crDuration = cr->durationType();
129 74524 : const Groups& g = cr->staff()->group(cr->tick());
130 74524 : Fraction stretch = cr->staff()->timeStretch(cr->tick());
131 74524 : Fraction tick = cr->rtick() * stretch + cr->measure()->anacrusisOffset();
132 :
133 : // We can choose to break beams based on its place in the measure, or by its duration. These
134 : // can be consolidated mostly, with bias towards its duration.
135 74524 : BeamMode byType = g.beamMode(tick.ticks(), crDuration.type());
136 74524 : BeamMode byPos = g.beamMode(tick.ticks(), bigBeatDuration);
137 74524 : BeamMode val = byType == BeamMode::AUTO ? byPos : byType;
138 :
139 : // context-dependent checks
140 74524 : if (val == BeamMode::AUTO && tick.isNotZero()) {
141 : // if current or previous cr is in tuplet (but not both in same tuplet):
142 : // consider it as if this were next shorter duration
143 60080 : if (prev && (cr->tuplet() != prev->tuplet()) && (crDuration == prev->durationType())) {
144 505 : if (crDuration >= DurationType::V_EIGHTH) {
145 224 : val = g.beamMode(tick.ticks(), DurationType::V_16TH);
146 281 : } else if (crDuration == DurationType::V_16TH) {
147 83 : val = g.beamMode(tick.ticks(), DurationType::V_32ND);
148 : } else {
149 198 : val = g.beamMode(tick.ticks(), DurationType::V_64TH);
150 : }
151 : }
152 : // if there is a hole between previous and current cr, break beam
153 : // exclude tuplets from this check; tick calculations can be unreliable
154 : // and they seem to be handled well anyhow
155 60080 : if (cr->voice() && prev && !prev->tuplet() && prev->endTick() < cr->tick()) {
156 0 : val = BeamMode::BEGIN;
157 : }
158 : }
159 :
160 74524 : return val;
161 : }
162 :
163 : //---------------------------------------------------------
164 : // actualBeamMode
165 : // final beam mode after applying contextual corrections
166 : //---------------------------------------------------------
167 :
168 0 : BeamMode Groups::actualBeamMode(const ChordRest* cr, const ChordRest* prev)
169 : {
170 0 : Measure* measure = cr->measure();
171 0 : const Staff* stf = cr->staff();
172 0 : TimeSig* ts = stf->timeSig(measure->tick());
173 0 : bool checkBeats = ts && ts->denominator() == 4;
174 :
175 0 : std::unordered_map<int, TDuration> beatSubdivision;
176 0 : if (checkBeats && cr->rtick().isNotZero()) {
177 0 : Fraction stretch = ts->stretch();
178 0 : Fraction tick = cr->rtick() * stretch;
179 :
180 0 : if ((tick.ticks() % Constants::DIVISION) == 0) {
181 0 : for (Segment* s = measure->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
182 0 : ChordRest* mcr = toChordRest(s->element(cr->track()));
183 0 : if (mcr == 0) {
184 0 : continue;
185 : }
186 0 : int beat = (mcr->rtick() * stretch).ticks() / Constants::DIVISION;
187 0 : if (muse::contains(beatSubdivision, beat)) {
188 0 : beatSubdivision[beat] = std::min(beatSubdivision[beat], mcr->durationType());
189 : } else {
190 0 : beatSubdivision[beat] = mcr->durationType();
191 : }
192 : }
193 : }
194 : }
195 :
196 0 : return actualBeamMode(cr, prev, &beatSubdivision);
197 0 : }
198 :
199 85837 : BeamMode Groups::actualBeamMode(const ChordRest* cr, const ChordRest* prev,
200 : const std::unordered_map<int, TDuration>* beatSubdivision)
201 : {
202 : // do not beam rests set to BeamMode::AUTO or with only other rests
203 85837 : if (cr->isRest() && cr->beamMode() == BeamMode::AUTO) {
204 63 : return BeamMode::NONE;
205 : }
206 :
207 85774 : TDuration durationType = cr->durationType();
208 : // chord with no hooks cannot be beamed
209 85774 : if (cr->isChord() && durationType.hooks() == 0) {
210 31243 : return BeamMode::NONE;
211 : }
212 : // if chord has hooks and is 2nd element of a cross-measure value
213 : // set beam mode to NONE (do not combine with following chord beam/hook, if any)
214 54531 : if (durationType.hooks() > 0 && cr->crossMeasure() == CrossMeasure::SECOND) {
215 0 : return BeamMode::NONE;
216 : }
217 :
218 54531 : BeamMode bm = Groups::baseBeamMode(cr, prev);
219 :
220 54531 : if (bm == BeamMode::AUTO) {
221 : // start beam at the beginning of measures
222 11552 : if (cr->rtick().isZero()) {
223 1498 : return BeamMode::BEGIN;
224 : }
225 :
226 : // perform additional context-dependent checks
227 10054 : Measure* measure = cr->measure();
228 10054 : const Staff* stf = cr->staff();
229 10054 : TimeSig* ts = stf->timeSig(measure->tick());
230 10054 : bool checkBeats = ts && ts->denominator() == 4;
231 10054 : Fraction stretch = ts ? ts->stretch() : Fraction(1, 1);
232 :
233 : // check if we need to break beams according to minimum duration in current / previous beat
234 10054 : if (checkBeats) {
235 9263 : Fraction tick = cr->rtick() * stretch;
236 : // check if on the beat
237 9263 : if ((tick.ticks() % Constants::DIVISION) == 0) {
238 1684 : int beat = tick.ticks() / Constants::DIVISION;
239 : // get minimum duration for this & previous beat
240 1684 : auto it_current = beatSubdivision->find(beat);
241 1684 : auto it_prev = beatSubdivision->find(beat - 1);
242 1684 : TDuration current_min = (it_current != beatSubdivision->end()) ? it_current->second : TDuration(DurationType::V_INVALID);
243 1684 : TDuration prev_min = (it_prev != beatSubdivision->end()) ? it_prev->second : TDuration(DurationType::V_INVALID);
244 1684 : TDuration minDuration = std::min(current_min, prev_min);
245 : // re-calculate beam as if this were the duration of current chordrest
246 1684 : TDuration saveDuration = cr->actualDurationType();
247 1684 : TDuration saveCMDuration = cr->crossMeasureDurationType();
248 1684 : CrossMeasure saveCrossMeasVal = cr->crossMeasure();
249 1684 : const_cast<ChordRest*>(cr)->setDurationType(minDuration);
250 1684 : bm = Groups::baseBeamMode(cr, prev);
251 1684 : const_cast<ChordRest*>(cr)->setDurationType(saveDuration);
252 1684 : const_cast<ChordRest*>(cr)->setCrossMeasure(saveCrossMeasVal);
253 1684 : const_cast<ChordRest*>(cr)->setCrossMeasureDurationType(saveCMDuration);
254 : }
255 : }
256 : }
257 :
258 53033 : return bm == BeamMode::AUTO ? BeamMode::MID : bm;
259 : }
260 :
261 : //---------------------------------------------------------
262 : // beamMode
263 : // tick is relative to begin of measure
264 : //---------------------------------------------------------
265 :
266 153351 : BeamMode Groups::beamMode(int tick, DurationType d) const
267 : {
268 : int shift;
269 153351 : switch (d) {
270 104515 : case DurationType::V_EIGHTH: shift = 0;
271 104515 : break;
272 33342 : case DurationType::V_16TH: shift = 4;
273 33342 : break;
274 15444 : case DurationType::V_32ND:
275 : // All beams shorter than 32nds break in the same place
276 : case DurationType::V_64TH:
277 : case DurationType::V_128TH:
278 : case DurationType::V_256TH:
279 : case DurationType::V_512TH:
280 : case DurationType::V_1024TH:
281 15444 : shift = 8;
282 15444 : break;
283 50 : default:
284 50 : return BeamMode::AUTO;
285 : }
286 153301 : const int dm = Constants::DIVISION / 8;
287 553710 : for (const GroupNode& e : m_nodes) {
288 547202 : if (e.pos * dm < tick) {
289 400409 : continue;
290 : }
291 146793 : if (e.pos * dm > tick) {
292 65050 : break;
293 : }
294 :
295 81743 : int action = (e.action >> shift) & 0xf;
296 81743 : switch (action) {
297 81743 : case 0: return BeamMode::AUTO;
298 13721 : case 1: return BeamMode::BEGIN;
299 1167 : case 2: return BeamMode::BEGIN16;
300 0 : case 3: return BeamMode::BEGIN32;
301 0 : default:
302 0 : LOGD(" Groups::beamMode: bad action %d", action);
303 0 : return BeamMode::AUTO;
304 : }
305 : }
306 71558 : return BeamMode::AUTO;
307 : }
308 :
309 : //---------------------------------------------------------
310 : // endings
311 : //---------------------------------------------------------
312 :
313 152365 : const Groups& Groups::endings(const Fraction& f)
314 : {
315 942147 : for (const NoteGroup& g : noteGroups) {
316 942144 : if (g.timeSig.identical(f)) {
317 152362 : return g.endings;
318 : }
319 : }
320 3 : NoteGroup g;
321 3 : g.timeSig = f;
322 3 : noteGroups.push_back(g);
323 :
324 3 : int pos = 0;
325 3 : switch (f.denominator()) {
326 0 : case 2: pos = 16;
327 0 : break;
328 1 : case 4: pos = 8;
329 1 : break;
330 1 : case 8: pos = 4;
331 1 : break;
332 1 : case 16: pos = 2;
333 1 : break;
334 0 : case 32: pos = 1;
335 0 : break;
336 : break;
337 : }
338 27 : for (int i = 1; i < f.numerator(); ++i) {
339 24 : GroupNode n;
340 24 : n.pos = pos * i;
341 24 : n.action = 0x111;
342 24 : g.endings.addNode(n);
343 : }
344 3 : return noteGroups.back().endings;
345 3 : }
346 :
347 : //---------------------------------------------------------
348 : // addStop
349 : //---------------------------------------------------------
350 :
351 0 : void Groups::addStop(int pos, DurationType d, BeamMode bm)
352 : {
353 : int shift;
354 0 : switch (d) {
355 0 : case DurationType::V_EIGHTH: shift = 0;
356 0 : break;
357 0 : case DurationType::V_16TH: shift = 4;
358 0 : break;
359 0 : case DurationType::V_32ND: shift = 8;
360 0 : break;
361 0 : default:
362 0 : return;
363 : }
364 : int action;
365 0 : if (bm == BeamMode::BEGIN) {
366 0 : action = 1;
367 0 : } else if (bm == BeamMode::BEGIN16) {
368 0 : action = 2;
369 0 : } else if (bm == BeamMode::BEGIN32) {
370 0 : action = 3;
371 : } else {
372 0 : return;
373 : }
374 :
375 0 : pos /= 60;
376 0 : action <<= shift;
377 :
378 0 : auto i = m_nodes.begin();
379 0 : for (; i != m_nodes.end(); ++i) {
380 0 : if (i->pos == pos) {
381 0 : i->action = (i->action & ~(0xf << shift)) | action;
382 0 : return;
383 : }
384 0 : if (i->pos > pos) {
385 0 : break;
386 : }
387 : }
388 0 : m_nodes.insert(i, GroupNode({ pos, action }));
389 : }
390 :
391 : //---------------------------------------------------------
392 : // dump
393 : //---------------------------------------------------------
394 :
395 0 : void Groups::dump(const char* m) const
396 : {
397 0 : LOGD("%s", m);
398 0 : for (const GroupNode& n : m_nodes) {
399 0 : LOGD(" group tick %d action 0x%02x", n.pos * 60, n.action);
400 : }
401 0 : }
402 : }
|