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 "containers.h"
24 :
25 : #include "iengravingconfiguration.h" // IWYU pragma: keep
26 :
27 : #include "types/typesconv.h"
28 :
29 : #include "barline.h"
30 : #include "clef.h"
31 : #include "cleflist.h"
32 : #include "excerpt.h"
33 : #include "factory.h"
34 : #include "instrtemplate.h"
35 : #include "linkedobjects.h"
36 : #include "masterscore.h"
37 : #include "measure.h"
38 : #include "mscore.h"
39 : #include "note.h"
40 : #include "ottava.h"
41 : #include "part.h"
42 : #include "score.h"
43 : #include "segment.h"
44 : #include "staff.h"
45 : #include "stafftype.h"
46 : #include "timesig.h"
47 : #include "utils.h"
48 :
49 : #include "../editing/editstaffbrackets.h"
50 : #include "../editing/editcapo.h"
51 : #include "../editing/transpose.h"
52 :
53 : // #define DEBUG_CLEFS
54 :
55 : #include "log.h"
56 :
57 : using namespace mu;
58 : using namespace mu::engraving;
59 :
60 : #ifdef DEBUG_CLEFS
61 : #define DUMP_CLEFS(s) dumpClefs(s)
62 : #else
63 : #define DUMP_CLEFS(s)
64 : #endif
65 :
66 : namespace mu::engraving {
67 : //---------------------------------------------------------
68 : // Staff
69 : //---------------------------------------------------------
70 :
71 3558 : Staff::Staff(Part* parent)
72 17790 : : EngravingItem(ElementType::STAFF, parent)
73 : {
74 3558 : m_color = configuration()->defaultColor();
75 3558 : initFromStaffType(0);
76 3558 : }
77 :
78 : //---------------------------------------------------------
79 : // copy ctor
80 : //---------------------------------------------------------
81 :
82 2 : Staff::Staff(const Staff& staff)
83 10 : : EngravingItem(staff)
84 : {
85 2 : init(&staff);
86 2 : m_part = staff.m_part;
87 2 : }
88 :
89 : //---------------------------------------------------------
90 : // clone
91 : //---------------------------------------------------------
92 :
93 2 : Staff* Staff::clone() const
94 : {
95 2 : return new Staff(*this);
96 : }
97 :
98 : //---------------------------------------------------------
99 : // idx
100 : //---------------------------------------------------------
101 :
102 376778 : staff_idx_t Staff::idx() const
103 : {
104 376778 : return muse::indexOf(score()->staves(), (Staff*)this);
105 : }
106 :
107 263134 : Part* Staff::originPart(const Fraction& tick) const
108 : {
109 : // Hides EngravingItem::originPart, whose track and tick are meaningless for a staff
110 : // Always returns part of voice 0
111 263134 : if (!m_part || !m_part->isSharedPart()) {
112 261216 : return m_part;
113 : }
114 :
115 1918 : return m_part->originPart(staff2track(idx(), 0), tick);
116 : }
117 :
118 : //---------------------------------------------------------
119 : // triggerLayout
120 : //---------------------------------------------------------
121 :
122 411 : void Staff::triggerLayout() const
123 : {
124 411 : score()->setLayoutAll(idx(), this);
125 411 : }
126 :
127 36 : void Staff::triggerLayout(const Fraction& tick)
128 : {
129 36 : score()->setLayout(tick, idx());
130 36 : }
131 :
132 105 : Staff* Staff::findLinkedInScore(const Score* score) const
133 : {
134 163 : for (Staff* staff : score->staves()) {
135 132 : if (staff->id() == id()) {
136 74 : return staff;
137 : }
138 : }
139 :
140 31 : return nullptr;
141 : }
142 :
143 5979 : track_idx_t Staff::getLinkedTrackInStaff(const Staff* linkedStaff, const track_idx_t originalTrack) const
144 : {
145 5979 : IF_ASSERT_FAILED(linkedStaff && originalTrack != muse::nidx) {
146 0 : return muse::nidx;
147 : }
148 :
149 5979 : Score* thisScore = score();
150 5979 : Score* linkedStaffScore = linkedStaff->score();
151 5979 : staff_idx_t linkedStaffIdx = linkedStaff->idx();
152 :
153 5979 : if (thisScore == linkedStaffScore) {
154 : // For staves linked within the same score, voices are always mapped 1 to 1
155 5877 : voice_idx_t voice = track2voice(originalTrack);
156 5877 : return staff2track(linkedStaffIdx, voice);
157 : }
158 :
159 : // NOTE 1: if linkedStaff has a different id than *this, it means that there isn't direct voice mapping between
160 : // the two. Example: if we have guitar+TAB in the score, with corresponding guitar+TAB in the part, *this
161 : // may be the notation-staff of the score and linkedStaff may be the TAB-staff of the part. In that case, the
162 : // correct voice mapping must be obtained by looking for the staff with same id in thisScore.
163 :
164 102 : track_idx_t refTrack = originalTrack;
165 102 : if (linkedStaff->id() != id()) {
166 2 : Staff* correspondingStaffInThisScore = linkedStaff->findLinkedInScore(thisScore);
167 2 : if (!correspondingStaffInThisScore) {
168 0 : return muse::nidx;
169 : }
170 2 : voice_idx_t originalVoice = track2voice(originalTrack);
171 2 : refTrack = staff2track(correspondingStaffInThisScore->idx(), originalVoice);
172 : }
173 :
174 : // NOTE 2: TracksMap is a map from a track in the *score* to track(s) in the *part*.
175 : // If we are in the master score, refTrack corresponds to one of the keys in the map, so we can retrieve
176 : // the linked tracks by simply querying the map by key. However, if we are in a part, we need to search for
177 : // refTrack among the *values* of the map, and the corresponding key gives us the linked track in the score.
178 : // If linkedStaff is *also* in a part, we need to do it in two steps: first find the linked track in the score,
179 : // then use it to find the linked track in linkeStaff's part.
180 :
181 102 : if (thisScore->isMaster()) {
182 92 : const TracksMap& tracksMap = linkedStaffScore->excerpt()->tracksMapping();
183 92 : std::vector<track_idx_t> linkedTracks = muse::values(tracksMap, refTrack);
184 92 : for (track_idx_t track : linkedTracks) {
185 90 : if (track2staff(track) == linkedStaffIdx) {
186 90 : return track;
187 : }
188 : }
189 :
190 2 : return muse::nidx;
191 92 : }
192 :
193 10 : const TracksMap& thisTracksMap = thisScore->excerpt()->tracksMapping();
194 10 : track_idx_t linkedTrackInScore = muse::nidx;
195 24 : for (auto pair : thisTracksMap) {
196 24 : track_idx_t trackInScore = pair.first;
197 24 : std::vector<track_idx_t> tracksInPart = muse::values(thisTracksMap, trackInScore);
198 38 : for (track_idx_t trackInPart : tracksInPart) {
199 24 : if (trackInPart == refTrack) {
200 10 : linkedTrackInScore = trackInScore;
201 10 : break;
202 : }
203 : }
204 24 : if (linkedTrackInScore != muse::nidx) {
205 10 : break;
206 : }
207 24 : }
208 :
209 10 : if (linkedStaffScore->isMaster() || linkedTrackInScore == muse::nidx) {
210 10 : return linkedTrackInScore;
211 : }
212 :
213 0 : const TracksMap& linkedTracksMap = linkedStaffScore->excerpt()->tracksMapping();
214 0 : std::vector<track_idx_t> linkedTracks = muse::values(linkedTracksMap, linkedTrackInScore);
215 0 : for (track_idx_t track : linkedTracks) {
216 0 : if (track2staff(track) == linkedStaffIdx) {
217 0 : return track;
218 : }
219 : }
220 :
221 0 : return muse::nidx;
222 0 : }
223 :
224 17 : bool Staff::trackHasLinksInVoiceZero(track_idx_t track)
225 : {
226 34 : for (Staff* linkedStaff : staffList()) {
227 17 : track_idx_t linkedTrack = getLinkedTrackInStaff(linkedStaff, track);
228 17 : if (linkedTrack != muse::nidx && track2voice(linkedTrack) == 0) {
229 0 : return true;
230 : }
231 17 : }
232 :
233 17 : return false;
234 : }
235 :
236 0 : void Staff::undoSetShowMeasureNumbers(bool show)
237 : {
238 0 : bool isTopStave = score()->staves().front() == this;
239 0 : if (show) {
240 0 : undoChangeProperty(Pid::SHOW_MEASURE_NUMBERS, isTopStave ? AutoOnOff::AUTO : AutoOnOff::ON);
241 : } else {
242 0 : undoChangeProperty(Pid::SHOW_MEASURE_NUMBERS, isTopStave ? AutoOnOff::OFF : AutoOnOff::AUTO);
243 : }
244 0 : }
245 :
246 17098 : bool Staff::shouldShowMeasureNumbers() const
247 : {
248 17098 : MeasureNumberPlacement placementMode = style().styleV(Sid::measureNumberPlacementMode).value<MeasureNumberPlacement>();
249 17098 : switch (placementMode) {
250 17098 : case MeasureNumberPlacement::ABOVE_SYSTEM:
251 17098 : return score()->staves().front() == this;
252 0 : case MeasureNumberPlacement::BELOW_SYSTEM:
253 0 : return score()->staves().back() == this;
254 0 : case MeasureNumberPlacement::ON_SYSTEM_OBJECT_STAVES:
255 : {
256 0 : bool isTopStave = score()->staves().front() == this;
257 0 : return (isTopStave && m_showMeasureNumbers != AutoOnOff::OFF) || (isSystemObjectStaff() && m_showMeasureNumbers == AutoOnOff::ON);
258 : }
259 0 : case MeasureNumberPlacement::ON_ALL_STAVES:
260 0 : return show();
261 : }
262 :
263 0 : return false;
264 : }
265 :
266 1320 : bool Staff::isLastOfScore() const
267 : {
268 1320 : return score()->staves().empty() ? false : score()->staves().back() == this;
269 : }
270 :
271 49610 : bool Staff::isSystemObjectStaff() const
272 : {
273 49610 : return score() && muse::contains(score()->systemObjectStaves(), const_cast<Staff*>(this));
274 : }
275 :
276 49295 : bool Staff::hasSystemObjectsBelowBottomStaff() const
277 : {
278 49295 : return isSystemObjectStaff() && isLastOfScore() && style().styleB(Sid::systemObjectsBelowBottomStaff);
279 : }
280 :
281 5652 : String Staff::individualStaffNameLong(const Fraction& tick) const
282 : {
283 5652 : IF_ASSERT_FAILED(m_part) {
284 0 : return String();
285 : }
286 :
287 5652 : return staffType(tick)->longName();
288 : }
289 :
290 3838 : String Staff::individualStaffNameShort(const Fraction& tick) const
291 : {
292 3838 : IF_ASSERT_FAILED(m_part) {
293 0 : return String();
294 : }
295 :
296 3838 : return staffType(tick)->shortName();
297 : }
298 :
299 7876 : bool Staff::playbackVoice(int voice) const
300 : {
301 7876 : return m_playbackVoice[voice];
302 : }
303 :
304 1 : void Staff::setPlaybackVoice(int voice, bool val)
305 : {
306 1 : m_playbackVoice[voice] = val;
307 1 : }
308 :
309 521 : const std::array<bool, VOICES>& Staff::visibilityVoices() const
310 : {
311 521 : return m_visibilityVoices;
312 : }
313 :
314 21 : bool Staff::isVoiceVisible(voice_idx_t voice) const
315 : {
316 21 : if (voice >= VOICES) {
317 0 : return false;
318 : }
319 :
320 21 : return m_visibilityVoices[voice];
321 : }
322 :
323 18 : void Staff::setVoiceVisible(voice_idx_t voice, bool visible)
324 : {
325 18 : if (voice >= VOICES) {
326 0 : return;
327 : }
328 :
329 18 : m_visibilityVoices[voice] = visible;
330 : }
331 :
332 0 : bool Staff::canDisableVoice() const
333 : {
334 0 : auto voices = visibilityVoices();
335 0 : int countOfVisibleVoices = 0;
336 0 : for (size_t i = 0; i < voices.size(); ++i) {
337 0 : if (voices[i]) {
338 0 : countOfVisibleVoices++;
339 : }
340 : }
341 :
342 0 : return countOfVisibleVoices > 1;
343 : }
344 :
345 184 : void Staff::updateVisibilityVoices(const Staff* masterStaff, const TracksMap& tracks)
346 : {
347 184 : if (tracks.empty()) {
348 1 : m_visibilityVoices = { true, true, true, true };
349 1 : return;
350 : }
351 :
352 183 : std::array<bool, VOICES> voices{ false, false, false, false };
353 :
354 183 : staff_idx_t masterStaffIdx = masterStaff->idx();
355 183 : staff_idx_t staffIdx = idx();
356 :
357 183 : voice_idx_t voiceIndex = 0;
358 915 : for (voice_idx_t voice = 0; voice < VOICES; voice++) {
359 732 : std::vector<track_idx_t> masterStaffTracks = muse::values(tracks, masterStaffIdx * VOICES + voice % VOICES);
360 732 : bool isVoiceVisible = muse::contains(masterStaffTracks, staffIdx * VOICES + voiceIndex % VOICES);
361 732 : if (isVoiceVisible) {
362 715 : voices[voice] = true;
363 715 : voiceIndex++;
364 : }
365 732 : }
366 :
367 183 : m_visibilityVoices = voices;
368 : }
369 :
370 1145 : bool Staff::reflectTranspositionInLinkedTab() const
371 : {
372 1145 : return m_reflectTranspositionInLinkedTab;
373 : }
374 :
375 6 : void Staff::setReflectTranspositionInLinkedTab(bool reflect)
376 : {
377 6 : m_reflectTranspositionInLinkedTab = reflect;
378 6 : }
379 :
380 : //---------------------------------------------------------
381 : // partName
382 : //---------------------------------------------------------
383 :
384 161 : String Staff::partName() const
385 : {
386 161 : return m_part->partName();
387 : }
388 :
389 : //---------------------------------------------------------
390 : // Staff::clefType
391 : //---------------------------------------------------------
392 :
393 89659 : ClefTypeList Staff::clefType(const Fraction& tick) const
394 : {
395 89659 : ClefTypeList ct = m_clefs.clef(tick.ticks());
396 89659 : if (ct.concertClef == ClefType::INVALID) {
397 : // Clef compatibility based on instrument (override StaffGroup)
398 54509 : StaffGroup staffGroup = staffType(tick)->group();
399 54509 : if (staffGroup != StaffGroup::TAB) {
400 53847 : staffGroup = originPart(tick)->instrument(tick)->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
401 : }
402 :
403 54509 : switch (staffGroup) {
404 662 : case StaffGroup::TAB:
405 : {
406 662 : ClefType sct = ClefType(style().styleI(Sid::tabClef));
407 662 : ct = staffType(tick)->lines() <= 4 ? ClefTypeList(sct == ClefType::TAB ? ClefType::TAB4 : ClefType::TAB4_SERIF) : ClefTypeList(
408 : sct == ClefType::TAB ? ClefType::TAB : ClefType::TAB_SERIF);
409 : }
410 662 : break;
411 52987 : case StaffGroup::STANDARD:
412 52987 : ct = defaultClefType();
413 52987 : break;
414 860 : case StaffGroup::PERCUSSION:
415 860 : ct = ClefTypeList(ClefType::PERC);
416 860 : break;
417 : }
418 : }
419 89659 : return ct;
420 : }
421 :
422 : //---------------------------------------------------------
423 : // Staff::clef
424 : //---------------------------------------------------------
425 :
426 71557 : ClefType Staff::clef(const Fraction& tick) const
427 : {
428 71557 : ClefTypeList c = clefType(tick);
429 143114 : return style().styleB(Sid::concertPitch) ? c.concertClef : c.transposingClef;
430 : }
431 :
432 : //---------------------------------------------------------
433 : // Staff::nextClefTick
434 : //
435 : // return the tick of next clef after tick
436 : // return last tick of score if not found
437 : //---------------------------------------------------------
438 :
439 57 : Fraction Staff::nextClefTick(const Fraction& tick) const
440 : {
441 57 : Fraction t = Fraction::fromTicks(m_clefs.nextClefTick(tick.ticks()));
442 57 : return t != Fraction(-1, 1) ? t : score()->endTick();
443 : }
444 :
445 : //---------------------------------------------------------
446 : // Staff::currentClefTick
447 : //
448 : // return the tick position of the clef currently
449 : // in effect at tick
450 : // return 0, if no such clef
451 : //---------------------------------------------------------
452 :
453 13300 : Fraction Staff::currentClefTick(const Fraction& tick) const
454 : {
455 13300 : return Fraction::fromTicks(m_clefs.currentClefTick(tick.ticks()));
456 : }
457 :
458 0 : String Staff::staffName() const
459 : {
460 0 : return TConv::translatedUserName(clefType(Fraction()).transposingClef);
461 : }
462 :
463 : #ifndef NDEBUG
464 : //---------------------------------------------------------
465 : // dumpClef
466 : //---------------------------------------------------------
467 :
468 0 : void Staff::dumpClefs(const char* title) const
469 : {
470 0 : LOGD("(%zd): %s", m_clefs.size(), title);
471 0 : for (auto& i : m_clefs) {
472 0 : LOGD(" %d: %d %d", i.first, int(i.second.concertClef), int(i.second.transposingClef));
473 : }
474 0 : }
475 :
476 : //---------------------------------------------------------
477 : // dumpKeys
478 : //---------------------------------------------------------
479 :
480 0 : void Staff::dumpKeys(const char* title) const
481 : {
482 0 : LOGD("(%zd): %s", m_keys.size(), title);
483 0 : for (auto& i : m_keys) {
484 0 : LOGD(" %d: %d", i.first, int(i.second.key()));
485 : }
486 0 : }
487 :
488 : //---------------------------------------------------------
489 : // dumpTimeSigs
490 : //---------------------------------------------------------
491 :
492 0 : void Staff::dumpTimeSigs(const char* title) const
493 : {
494 0 : LOGD("size (%zd) staffIdx %zu: %s", m_timesigs.size(), idx(), title);
495 0 : for (auto& i : m_timesigs) {
496 0 : LOGD(" %d: %d/%d", i.first, i.second->sig().numerator(), i.second->sig().denominator());
497 : }
498 0 : }
499 :
500 : #endif
501 :
502 : //---------------------------------------------------------
503 : // setClef
504 : //---------------------------------------------------------
505 :
506 1644 : void Staff::setClef(Clef* clef)
507 : {
508 1644 : if (clef->generated()) {
509 0 : return;
510 : }
511 1644 : Fraction tick = clef->segment()->tick();
512 3835 : for (Segment* s = clef->segment()->next1(); s && s->tick() == tick; s = s->next1()) {
513 4380 : if ((s->segmentType() == SegmentType::Clef || s->segmentType() == SegmentType::HeaderClef)
514 14 : && s->element(clef->track())
515 4382 : && !s->element(clef->track())->generated()) {
516 : // adding this clef has no effect on the clefs list
517 0 : return;
518 : }
519 : }
520 1644 : m_clefs.setClef(clef->segment()->tick().ticks(), clef->clefTypeList());
521 : DUMP_CLEFS("setClef");
522 : }
523 :
524 : //---------------------------------------------------------
525 : // removeClef
526 : //---------------------------------------------------------
527 :
528 71 : void Staff::removeClef(const Clef* clef)
529 : {
530 71 : if (clef->generated()) {
531 52 : return;
532 : }
533 19 : Fraction tick = clef->segment()->tick();
534 67 : for (Segment* s = clef->segment()->next1(); s && s->tick() == tick; s = s->next1()) {
535 96 : if ((s->segmentType() == SegmentType::Clef || s->segmentType() == SegmentType::HeaderClef)
536 0 : && s->element(clef->track())
537 96 : && !s->element(clef->track())->generated()) {
538 : // removal of this clef has no effect on the clefs list
539 0 : return;
540 : }
541 : }
542 19 : m_clefs.erase(clef->segment()->tick().ticks());
543 30 : for (Segment* s = clef->segment()->prev1(); s && s->tick() == tick; s = s->prev1()) {
544 22 : if ((s->segmentType() == SegmentType::Clef || s->segmentType() == SegmentType::HeaderClef)
545 1 : && s->element(clef->track())
546 22 : && !s->element(clef->track())->generated()) {
547 : // a previous clef at the same tick position gets valid
548 0 : m_clefs.setClef(tick.ticks(), toClef(s->element(clef->track()))->clefTypeList());
549 0 : break;
550 : }
551 : }
552 : DUMP_CLEFS("removeClef");
553 : }
554 :
555 : //---------------------------------------------------------
556 : // timeStretch
557 : //---------------------------------------------------------
558 :
559 1618214 : Fraction Staff::timeStretch(const Fraction& tick) const
560 : {
561 1618214 : TimeSig* timesig = timeSig(tick);
562 1618214 : return timesig ? timesig->stretch() : Fraction(1, 1);
563 : }
564 :
565 : //---------------------------------------------------------
566 : // timeSig
567 : // lookup time signature before or at tick
568 : //---------------------------------------------------------
569 :
570 2044772 : TimeSig* Staff::timeSig(const Fraction& tick) const
571 : {
572 2044772 : auto i = m_timesigs.upper_bound(tick.ticks());
573 2044772 : if (i != m_timesigs.begin()) {
574 2016692 : --i;
575 : }
576 2044772 : if (i == m_timesigs.end()) {
577 27787 : return 0;
578 2016985 : } else if (tick < Fraction::fromTicks(i->first)) {
579 293 : return 0;
580 : }
581 2016692 : return i->second;
582 : }
583 :
584 : //---------------------------------------------------------
585 : // nextTimeSig
586 : // lookup time signature at tick or after
587 : //---------------------------------------------------------
588 :
589 2 : TimeSig* Staff::nextTimeSig(const Fraction& tick) const
590 : {
591 2 : auto i = m_timesigs.lower_bound(tick.ticks());
592 2 : return (i == m_timesigs.end()) ? 0 : i->second;
593 : }
594 :
595 : //---------------------------------------------------------
596 : // currentTimeSigTick
597 : //
598 : // return the tick position of the time sig currently
599 : // in effect at tick
600 : //---------------------------------------------------------
601 :
602 0 : Fraction Staff::currentTimeSigTick(const Fraction& tick) const
603 : {
604 0 : if (m_timesigs.empty()) {
605 0 : return Fraction(0, 1);
606 : }
607 0 : auto i = m_timesigs.upper_bound(tick.ticks());
608 0 : if (i == m_timesigs.begin()) {
609 0 : return Fraction(0, 1);
610 : }
611 0 : --i;
612 0 : return Fraction::fromTicks(i->first);
613 : }
614 :
615 : //---------------------------------------------------------
616 : // group
617 : //---------------------------------------------------------
618 :
619 152365 : const Groups& Staff::group(const Fraction& tick) const
620 : {
621 152365 : TimeSig* ts = timeSig(tick);
622 152365 : if (ts) {
623 152335 : if (!ts->groups().empty()) {
624 0 : return ts->groups();
625 : }
626 152335 : return Groups::endings(ts->sig());
627 : }
628 30 : Measure* m = score()->tick2measure(tick);
629 30 : return Groups::endings(m ? m->timesig() : Fraction(4, 4));
630 : }
631 :
632 : //---------------------------------------------------------
633 : // addTimeSig
634 : //---------------------------------------------------------
635 :
636 7029 : void Staff::addTimeSig(TimeSig* timesig)
637 : {
638 7029 : if (timesig->segment()->segmentType() == SegmentType::TimeSig) {
639 6788 : m_timesigs[timesig->segment()->tick().ticks()] = timesig;
640 : }
641 : // dumpTimeSigs("after addTimeSig");
642 7029 : }
643 :
644 : //---------------------------------------------------------
645 : // removeTimeSig
646 : //---------------------------------------------------------
647 :
648 3364 : void Staff::removeTimeSig(TimeSig* timesig)
649 : {
650 3364 : if (timesig->segment()->segmentType() == SegmentType::TimeSig) {
651 3170 : if (m_timesigs[timesig->segment()->tick().ticks()] == timesig) {
652 3134 : m_timesigs.erase(timesig->segment()->tick().ticks());
653 : }
654 : }
655 : // dumpTimeSigs("after removeTimeSig");
656 3364 : }
657 :
658 : //---------------------------------------------------------
659 : // clearTimeSig
660 : //---------------------------------------------------------
661 :
662 3372 : void Staff::clearTimeSig()
663 : {
664 3372 : m_timesigs.clear();
665 3372 : }
666 :
667 : //---------------------------------------------------------
668 : // Staff::transpose
669 : //
670 : // actual staff transposioton at tick
671 : // (taking key into account)
672 : //---------------------------------------------------------
673 :
674 13876 : Interval Staff::transpose(const Fraction& tick) const
675 : {
676 : // get real transposition
677 :
678 13876 : const Part* p = originPart(tick);
679 13876 : Interval v = p->instrument(tick)->transpose();
680 13876 : if (v.isZero()) {
681 10205 : return v;
682 : }
683 3671 : Key cKey = concertKey(tick);
684 3671 : v.flip();
685 3671 : Key tKey = Transpose::transposeKey(cKey, v, p->preferSharpFlat());
686 3671 : v.flip();
687 :
688 3671 : int chromatic = (7 * (static_cast<int>(cKey) - static_cast<int>(tKey))) % 12;
689 3671 : if (chromatic < 0) {
690 716 : chromatic += 12;
691 : }
692 3671 : int diatonic = (4 * (static_cast<int>(cKey) - static_cast<int>(tKey))) % 7;
693 3671 : if (diatonic < 0) {
694 716 : diatonic += 7;
695 : }
696 :
697 3671 : if (v.chromatic < 0 || v.diatonic < 0) {
698 3661 : chromatic -= 12;
699 3661 : diatonic -= 7;
700 : }
701 :
702 3671 : v.chromatic = v.chromatic - (v.chromatic % 12) + (chromatic % 12);
703 3671 : v.diatonic = v.diatonic - (v.diatonic % 7) + (diatonic % 7);
704 :
705 3671 : return v;
706 : }
707 :
708 : //---------------------------------------------------------
709 : // Staff::keySigEvent
710 : //
711 : // locates the key sig currently in effect at tick
712 : //---------------------------------------------------------
713 :
714 1786807 : KeySigEvent Staff::keySigEvent(const Fraction& tick) const
715 : {
716 1786807 : return m_keys.key(tick.ticks());
717 : }
718 :
719 : //---------------------------------------------------------
720 : // setKey
721 : //---------------------------------------------------------
722 :
723 3190 : void Staff::setKey(const Fraction& tick, KeySigEvent k)
724 : {
725 3190 : m_keys.setKey(tick.ticks(), k);
726 3190 : }
727 :
728 : //---------------------------------------------------------
729 : // removeKey
730 : //---------------------------------------------------------
731 :
732 71 : void Staff::removeKey(const Fraction& tick)
733 : {
734 71 : m_keys.erase(tick.ticks());
735 71 : }
736 :
737 : //---------------------------------------------------------
738 : // prevkey
739 : //---------------------------------------------------------
740 :
741 0 : KeySigEvent Staff::prevKey(const Fraction& tick) const
742 : {
743 0 : return m_keys.prevKey(tick.ticks());
744 : }
745 :
746 : //---------------------------------------------------------
747 : // Staff::nextKeyTick
748 : //
749 : // return the tick at which the key sig after tick is located
750 : // return 0, if no such a key sig
751 : //---------------------------------------------------------
752 :
753 363 : Fraction Staff::nextKeyTick(const Fraction& tick) const
754 : {
755 363 : Fraction t = Fraction::fromTicks(m_keys.nextKeyTick(tick.ticks()));
756 363 : return t != Fraction(-1, 1) ? t : score()->endTick();
757 : }
758 :
759 : //---------------------------------------------------------
760 : // Staff::currentKeyTick
761 : //
762 : // return the tick position of the key currently
763 : // in effect at tick
764 : // return 0, if no such a key sig
765 : //---------------------------------------------------------
766 :
767 57923 : Fraction Staff::currentKeyTick(const Fraction& tick) const
768 : {
769 57923 : return Fraction::fromTicks(m_keys.currentKeyTick(tick.ticks()));
770 : }
771 :
772 : //---------------------------------------------------------
773 : // height
774 : //---------------------------------------------------------
775 :
776 32851 : double Staff::staffHeight() const
777 : {
778 32851 : Fraction tick = Fraction(0, 1);
779 32851 : return (lines(tick) - 1) * spatium(tick) * staffType(tick)->lineDistance().val();
780 : }
781 :
782 15199 : double Staff::staffHeight(const Fraction& tick) const
783 : {
784 15199 : return (lines(tick) - 1) * spatium(tick) * staffType(tick)->lineDistance().val();
785 : }
786 :
787 : //---------------------------------------------------------
788 : // spatium
789 : //---------------------------------------------------------
790 :
791 260954 : double Staff::spatium(const Fraction& tick) const
792 : {
793 260954 : return style().spatium() * staffMag(tick);
794 : }
795 :
796 3810151 : double Staff::spatium(const EngravingItem* e) const
797 : {
798 3810151 : return style().spatium() * staffMag(e);
799 : }
800 :
801 : //---------------------------------------------------------
802 : // mag
803 : //---------------------------------------------------------
804 :
805 11224818 : double Staff::staffMag(const StaffType* stt) const
806 : {
807 11224818 : return (stt->isSmall() ? style().styleD(Sid::smallStaffMag) : 1.0) * stt->userMag();
808 : }
809 :
810 582247 : double Staff::staffMag(const Fraction& tick) const
811 : {
812 582247 : return staffMag(staffType(tick));
813 : }
814 :
815 10642571 : double Staff::staffMag(const EngravingItem* element) const
816 : {
817 10642571 : return staffMag(staffTypeForElement(element));
818 : }
819 :
820 : //---------------------------------------------------------
821 : // swing
822 : //---------------------------------------------------------
823 :
824 1391 : SwingParameters Staff::swing(const Fraction& tick) const
825 : {
826 1391 : SwingParameters sp;
827 1391 : int swingUnit = 0;
828 1391 : muse::ByteArray ba = style().styleSt(Sid::swingUnit).toAscii();
829 1391 : DurationType unit = TConv::fromXml(ba.constChar(), DurationType::V_INVALID);
830 1391 : int swingRatio = style().styleI(Sid::swingRatio);
831 1391 : if (unit == DurationType::V_EIGHTH) {
832 0 : swingUnit = Constants::DIVISION / 2;
833 1391 : } else if (unit == DurationType::V_16TH) {
834 0 : swingUnit = Constants::DIVISION / 4;
835 1391 : } else if (unit == DurationType::V_ZERO) {
836 1391 : swingUnit = 0;
837 : }
838 1391 : sp.swingRatio = swingRatio;
839 1391 : sp.swingUnit = swingUnit;
840 1391 : if (m_swingMap.empty()) {
841 1391 : return sp;
842 : }
843 :
844 0 : auto it = muse::findLessOrEqual(m_swingMap, tick.ticks());
845 0 : return it == m_swingMap.cend() ? sp : it->second;
846 1391 : }
847 :
848 18290 : const CapoParams& Staff::capo(const Fraction& tick) const
849 : {
850 18290 : static const CapoParams dummy;
851 18290 : if (m_capoMap.empty()) {
852 17658 : return dummy;
853 : }
854 :
855 632 : auto it = muse::findLessOrEqual(m_capoMap, tick.ticks());
856 632 : return it == m_capoMap.cend() ? dummy : it->second;
857 : }
858 :
859 65 : void Staff::insertCapoParams(const Fraction& tick, const CapoParams& params, bool ignoreNotationUpdate)
860 : {
861 65 : if (ignoreNotationUpdate) {
862 13 : m_capoMap.insert_or_assign(tick.ticks(), params);
863 13 : return;
864 : }
865 26 : auto isNeedUpdate = [](const CapoParams& oldParams, const CapoParams& newParams) -> bool {
866 44 : return !(oldParams.active == newParams.active
867 18 : && oldParams.transposeMode == newParams.transposeMode
868 2 : && oldParams.fretPosition == newParams.fretPosition
869 28 : && oldParams.ignoredStrings == newParams.ignoredStrings);
870 : };
871 :
872 52 : int startTick = tick.ticks();
873 52 : int endTick = -1;
874 :
875 52 : if (auto it = m_capoMap.find(startTick); it == m_capoMap.end()) {
876 26 : auto result = m_capoMap.insert({ startTick, params });
877 26 : if (const auto nextIt = std::next(result.first); nextIt != m_capoMap.end()) {
878 0 : endTick = nextIt->first;
879 : }
880 26 : if (result.first != m_capoMap.begin()) {
881 6 : const auto prevIt = std::prev(result.first);
882 6 : CapoParams oldParams = prevIt->second;
883 : // We don't need to apply any changes if the previous capo is inactive
884 6 : if (oldParams.active) {
885 2 : EditCapo::updateNotationForCapoChange(oldParams, params, this, startTick, endTick);
886 : }
887 : // This is an undo action
888 26 : } else if (CapoParams::TransposeMode::PLAYBACK_ONLY != params.transposeMode
889 10 : && params.active) {
890 8 : CapoParams oldParams;
891 8 : oldParams.transposeMode = CapoParams::TransposeMode::PLAYBACK_ONLY;
892 8 : oldParams.fretPosition = params.fretPosition;
893 8 : oldParams.active = params.active;
894 8 : oldParams.ignoredStrings = params.ignoredStrings;
895 8 : EditCapo::updateNotationForCapoChange(oldParams, params, this, startTick, endTick);
896 8 : }
897 : } else {
898 26 : CapoParams oldParams = it->second;
899 26 : if (!isNeedUpdate(oldParams, params)) {
900 0 : return;
901 : }
902 26 : auto result = m_capoMap.insert_or_assign(startTick, params);
903 26 : if (const auto nextIt = std::next(result.first); nextIt != m_capoMap.end()) {
904 0 : endTick = nextIt->first;
905 : }
906 26 : EditCapo::updateNotationForCapoChange(oldParams, params, this, startTick, endTick);
907 26 : }
908 : }
909 :
910 10 : void Staff::removeCapoParams(const mu::engraving::Fraction& tick)
911 : {
912 10 : const int startTick = tick.ticks();
913 :
914 10 : const auto it = m_capoMap.find(startTick);
915 10 : IF_ASSERT_FAILED(it != m_capoMap.end()) {
916 0 : LOGE() << "Key must exist in capo map!";
917 0 : return;
918 : }
919 :
920 10 : const auto nextCapoIt = std::next(it);
921 10 : const int endTick = nextCapoIt == m_capoMap.end() ? -1 : nextCapoIt->first;
922 :
923 10 : CapoParams revertParams;
924 10 : revertParams.fretPosition = it->second.fretPosition;
925 :
926 10 : CapoParams oldParams = it->second;
927 : // If this capo is inactive, it is treated as PLAYBACK_ONLY mode capo
928 10 : if (!oldParams.active) {
929 2 : oldParams.transposeMode = CapoParams::TransposeMode::PLAYBACK_ONLY;
930 : }
931 :
932 10 : if (it != m_capoMap.begin()) {
933 6 : revertParams = std::prev(it)->second;
934 : // If not active, treat as PLAYBACK_ONLY
935 6 : if (!revertParams.active) {
936 4 : revertParams.transposeMode = CapoParams::TransposeMode::PLAYBACK_ONLY;
937 : }
938 : }
939 10 : EditCapo::updateNotationForCapoChange(oldParams, revertParams, this, startTick, endTick);
940 :
941 10 : m_capoMap.erase(startTick);
942 10 : }
943 :
944 2712 : bool Staff::shouldMergeMatchingRests() const
945 : {
946 2712 : return mergeMatchingRests() == AutoOnOff::ON
947 2712 : || (mergeMatchingRests() == AutoOnOff::AUTO && style().value(Sid::mergeMatchingRests).toBool());
948 : }
949 :
950 : //---------------------------------------------------------
951 : // channel
952 : //---------------------------------------------------------
953 :
954 19180 : int Staff::channel(const Fraction& tick, voice_idx_t voice) const
955 : {
956 19180 : const std::map<int, int>& map = m_channelList[voice];
957 19180 : if (map.empty()) {
958 17312 : return 0;
959 : }
960 :
961 1868 : auto it = muse::findLessOrEqual(map, tick.ticks());
962 1868 : return it == map.cend() ? 0 : it->second;
963 : }
964 :
965 : //---------------------------------------------------------
966 : // middleLine
967 : // returns logical line number of middle staff line
968 : //---------------------------------------------------------
969 :
970 30 : int Staff::middleLine(const Fraction& tick) const
971 : {
972 30 : return staffType(tick)->middleLine();
973 : }
974 :
975 : //---------------------------------------------------------
976 : // bottomLine
977 : // returns logical line number of bottom staff line
978 : //---------------------------------------------------------
979 :
980 0 : int Staff::bottomLine(const Fraction& tick) const
981 : {
982 0 : return staffType(tick)->bottomLine();
983 : }
984 :
985 : //---------------------------------------------------------
986 : // stemless
987 : //---------------------------------------------------------
988 :
989 2685601 : bool Staff::stemless(const Fraction& tick) const
990 : {
991 2685601 : return staffType(tick)->stemless();
992 : }
993 :
994 : //---------------------------------------------------------
995 : // setSlashStyle
996 : //---------------------------------------------------------
997 :
998 0 : void Staff::setSlashStyle(const Fraction& tick, bool val)
999 : {
1000 0 : staffType(tick)->setStemless(val);
1001 0 : }
1002 :
1003 : //---------------------------------------------------------
1004 : // isPrimaryStaff
1005 : /// if there are linked staves, the primary staff is
1006 : /// the one who is played back and it's not a tab staff
1007 : /// because we don't have enough information to play
1008 : /// e.g ornaments. NOTE: it's not necessarily the top staff!
1009 : //---------------------------------------------------------
1010 :
1011 7065 : bool Staff::isPrimaryStaff() const
1012 : {
1013 7065 : if (!m_links) {
1014 4636 : return true;
1015 : }
1016 :
1017 2429 : std::vector<const Staff*> linkedStavesInThisScore;
1018 2429 : std::vector<const Staff*> linkedNonTabStavesInThisScore;
1019 :
1020 7287 : for (const EngravingObject* linked : *m_links) {
1021 4858 : const Staff* staff = toStaff(linked);
1022 :
1023 4858 : if (staff->score() == score()) {
1024 4856 : linkedStavesInThisScore.push_back(staff);
1025 :
1026 4856 : if (!staff->isTabStaff(Fraction(0, 1))) {
1027 2431 : linkedNonTabStavesInThisScore.push_back(staff);
1028 : }
1029 : }
1030 : }
1031 :
1032 2429 : IF_ASSERT_FAILED(!linkedStavesInThisScore.empty()) {
1033 0 : return true;
1034 : }
1035 :
1036 2429 : if (!linkedNonTabStavesInThisScore.empty()) {
1037 2429 : return linkedNonTabStavesInThisScore.front() == this;
1038 : }
1039 :
1040 0 : return linkedStavesInThisScore.front() == this;
1041 2429 : }
1042 :
1043 : //---------------------------------------------------------
1044 : // staffType
1045 : //---------------------------------------------------------
1046 :
1047 8656190 : const StaffType* Staff::staffType(const Fraction& tick) const
1048 : {
1049 8656190 : return &m_staffTypeList.staffType(tick);
1050 : }
1051 :
1052 19057 : const StaffType* Staff::constStaffType(const Fraction& tick) const
1053 : {
1054 19057 : return &m_staffTypeList.staffType(tick);
1055 : }
1056 :
1057 389873 : StaffType* Staff::staffType(const Fraction& tick)
1058 : {
1059 389873 : return &m_staffTypeList.staffType(tick);
1060 : }
1061 :
1062 18018811 : const StaffType* Staff::staffTypeForElement(const EngravingItem* e) const
1063 : {
1064 18018811 : if (m_staffTypeList.uniqueStaffType()) {
1065 : // if one staff type spans for the entire staff, optimize by omitting a call to `tick()`
1066 17874393 : return &m_staffTypeList.staffType({ 0, 1 });
1067 : }
1068 : // Handle items at the last tick of measures as StaffTypeList::staffType rounds up
1069 144418 : const Measure* measure = e->findMeasure();
1070 144418 : const Fraction tick = measure ? measure->tick() : e->tick();
1071 144418 : return &m_staffTypeList.staffType(tick);
1072 : }
1073 :
1074 55670 : bool Staff::isStaffTypeStartFrom(const Fraction& tick) const
1075 : {
1076 55670 : return m_staffTypeList.isStaffTypeStartFrom(tick);
1077 : }
1078 :
1079 0 : void Staff::moveStaffType(const Fraction& from, const Fraction& to)
1080 : {
1081 0 : m_staffTypeList.moveStaffType(from, to);
1082 0 : staffTypeListChanged(from);
1083 0 : }
1084 :
1085 538 : std::pair<int, int> Staff::staffTypeRange(const Fraction& tick) const
1086 : {
1087 538 : return m_staffTypeList.staffTypeRange(tick);
1088 : }
1089 :
1090 : //---------------------------------------------------------
1091 : // staffTypeListChanged
1092 : // Signal that the staffTypeList has changed at
1093 : // position tick. Update layout range.
1094 : //---------------------------------------------------------
1095 :
1096 18 : void Staff::staffTypeListChanged(const Fraction& tick)
1097 : {
1098 18 : std::pair<int, int> range = m_staffTypeList.staffTypeRange(tick);
1099 :
1100 18 : if (range.first < 0) {
1101 0 : triggerLayout(Fraction(0, 1));
1102 : } else {
1103 18 : triggerLayout(Fraction::fromTicks(range.first));
1104 : }
1105 :
1106 18 : if (range.second < 0) {
1107 : // When reading a score and there is a Staff Change on the first
1108 : // measure, there are no measures yet and nothing to layout.
1109 18 : if (score()->lastMeasure()) {
1110 18 : triggerLayout(score()->lastMeasure()->endTick());
1111 : }
1112 : } else {
1113 0 : triggerLayout(Fraction::fromTicks(range.second));
1114 : }
1115 18 : }
1116 :
1117 : //---------------------------------------------------------
1118 : // setStaffType
1119 : //---------------------------------------------------------
1120 :
1121 6203 : StaffType* Staff::setStaffType(const Fraction& tick, const StaffType& nst)
1122 : {
1123 6203 : StaffType* stt = m_staffTypeList.setStaffType(tick, nst);
1124 6203 : stt->setScore(score());
1125 6203 : return stt;
1126 : }
1127 :
1128 : //---------------------------------------------------------
1129 : // setStaffType
1130 : //---------------------------------------------------------
1131 :
1132 0 : void Staff::removeStaffType(const Fraction& tick)
1133 : {
1134 0 : double old = spatium(tick);
1135 0 : const bool removed = m_staffTypeList.removeStaffType(tick);
1136 0 : if (!removed) {
1137 0 : return;
1138 : }
1139 0 : setLocalSpatium(old, spatium(tick), tick);
1140 0 : staffTypeListChanged(tick);
1141 : }
1142 :
1143 : //---------------------------------------------------------
1144 : // init
1145 : //---------------------------------------------------------
1146 :
1147 18 : void Staff::init(const InstrumentTemplate* t, const StaffType* staffType, staff_idx_t templateStaffIdx)
1148 : {
1149 : // Initialises a staff from an InstrumentTemplate and StaffType. The staff MUST be added to the score already.
1150 18 : assert(idx() != muse::nidx);
1151 : // set staff-type-independent parameters
1152 18 : const StaffType* pst = staffType ? staffType : t->staffTypePreset;
1153 18 : if (!pst) {
1154 3 : pst = StaffType::getDefaultPreset(t->staffGroup);
1155 : }
1156 :
1157 18 : const staff_idx_t staffIdx = idx() != muse::nidx ? idx() : score()->nstaves();
1158 :
1159 18 : StaffType* stt = setStaffType(Fraction(0, 1), *pst);
1160 18 : if (templateStaffIdx >= MAX_STAVES) {
1161 0 : stt->setSmall(false);
1162 : } else {
1163 18 : stt->setSmall(t->smallStaff[templateStaffIdx]);
1164 18 : EditStaffBrackets::setBracketType(score(), staffIdx, 0, t->bracket[templateStaffIdx]);
1165 18 : EditStaffBrackets::setBracketSpan(score(), staffIdx, 0, t->bracketSpan[templateStaffIdx]);
1166 18 : setBarLineSpan(t->barlineSpan[templateStaffIdx]);
1167 : }
1168 18 : setDefaultClefType(t->clefType(templateStaffIdx));
1169 18 : }
1170 :
1171 : //---------------------------------------------------------
1172 : // init
1173 : //---------------------------------------------------------
1174 :
1175 75 : void Staff::init(const Staff* s)
1176 : {
1177 75 : m_id = s->m_id;
1178 :
1179 75 : setStaffType(Fraction(0, 1), s->m_staffTypeList.staffType(Fraction(0, 1)));
1180 75 : for (const auto& stPair : s->m_staffTypeList.staffTypeChanges()) {
1181 0 : const StaffType& st = stPair.second;
1182 0 : StaffType newStaffType(st);
1183 0 : setStaffType(Fraction::fromTicks(stPair.first), newStaffType);
1184 0 : }
1185 :
1186 75 : setDefaultClefType(s->defaultClefType());
1187 75 : m_barLineFrom = s->m_barLineFrom;
1188 75 : m_barLineTo = s->m_barLineTo;
1189 75 : m_hideWhenEmpty = s->m_hideWhenEmpty;
1190 75 : m_cutaway = s->m_cutaway;
1191 75 : m_showIfEntireSystemEmpty = s->m_showIfEntireSystemEmpty;
1192 75 : m_hideSystemBarLine = s->m_hideSystemBarLine;
1193 75 : m_mergeMatchingRests = s->m_mergeMatchingRests;
1194 75 : m_color = s->m_color;
1195 75 : m_userDist = s->m_userDist;
1196 75 : m_visibilityVoices = s->m_visibilityVoices;
1197 75 : m_capoMap = s->m_capoMap;
1198 75 : }
1199 :
1200 68889 : const ID& Staff::id() const
1201 : {
1202 68889 : return m_id;
1203 : }
1204 :
1205 3732 : void Staff::setId(const ID& id)
1206 : {
1207 3732 : m_id = id;
1208 3732 : }
1209 :
1210 : //---------------------------------------------------------
1211 : // initFromStaffType
1212 : //---------------------------------------------------------
1213 :
1214 3596 : void Staff::initFromStaffType(const StaffType* staffType)
1215 : {
1216 : // get staff type if given (if none, get default preset for default staff group)
1217 3596 : if (!staffType) {
1218 3592 : staffType = StaffType::getDefaultPreset(StaffGroup::STANDARD);
1219 : }
1220 :
1221 : // use selected staff type
1222 3596 : setStaffType(Fraction(0, 1), *staffType);
1223 3596 : }
1224 :
1225 : //---------------------------------------------------------
1226 : // show
1227 : //---------------------------------------------------------
1228 :
1229 3304655 : bool Staff::show() const
1230 : {
1231 3304655 : return m_part->show() && visible();
1232 : }
1233 :
1234 : //---------------------------------------------------------
1235 : // genKeySig
1236 : //---------------------------------------------------------
1237 :
1238 0 : bool Staff::genKeySig()
1239 : {
1240 0 : if (constStaffType(Fraction(0, 1))->group() == StaffGroup::TAB) {
1241 0 : return false;
1242 : } else {
1243 0 : return constStaffType(Fraction(0, 1))->genKeysig();
1244 : }
1245 : }
1246 :
1247 : //---------------------------------------------------------
1248 : // showLedgerLines
1249 : //---------------------------------------------------------
1250 :
1251 14250 : bool Staff::showLedgerLines(const Fraction& tick) const
1252 : {
1253 14250 : return staffType(tick)->showLedgerLines();
1254 : }
1255 :
1256 : //---------------------------------------------------------
1257 : // color
1258 : //---------------------------------------------------------
1259 :
1260 44704 : Color Staff::color(const Fraction& tick) const
1261 : {
1262 44704 : return staffType(tick)->color();
1263 : }
1264 :
1265 : //---------------------------------------------------------
1266 : // setColor
1267 : //---------------------------------------------------------
1268 :
1269 0 : void Staff::setColor(const Fraction& tick, const Color& val)
1270 : {
1271 0 : staffType(tick)->setColor(val);
1272 0 : }
1273 :
1274 : //---------------------------------------------------------
1275 : // updateOttava
1276 : //---------------------------------------------------------
1277 :
1278 2475 : void Staff::updateOttava()
1279 : {
1280 2475 : staff_idx_t staffIdx = idx();
1281 2475 : m_pitchOffsetMap.clear();
1282 5121 : for (auto i : score()->spanner()) {
1283 2646 : const Spanner* s = i.second;
1284 2646 : if (s->isOttava() && s->staffIdx() == staffIdx && s->playSpanner()) {
1285 223 : const Ottava* o = toOttava(s);
1286 223 : m_pitchOffsetMap.insert_or_assign(o->tick().ticks(), o->pitchShift());
1287 223 : m_pitchOffsetMap.insert_or_assign(o->tick2().ticks(), 0);
1288 : }
1289 : }
1290 2475 : }
1291 :
1292 54630 : int Staff::pitchOffset(const Fraction& tick) const
1293 : {
1294 54630 : if (m_pitchOffsetMap.empty()) {
1295 52050 : return 0;
1296 : }
1297 :
1298 2580 : const auto it = muse::findLessOrEqual(m_pitchOffsetMap, tick.ticks());
1299 2580 : return it == m_pitchOffsetMap.cend() ? 0 : it->second;
1300 : }
1301 :
1302 : //---------------------------------------------------------
1303 : // insertTime
1304 : //---------------------------------------------------------
1305 :
1306 568 : void Staff::insertTime(const Fraction& tick, const Fraction& len)
1307 : {
1308 568 : if (len.isZero()) {
1309 9 : return;
1310 : }
1311 :
1312 : // move all keys and clefs >= tick
1313 :
1314 559 : if (len < Fraction(0, 1)) {
1315 : // remove entries between tickpos >= tick and tickpos < (tick+len)
1316 149 : m_keys.erase(m_keys.lower_bound(tick.ticks()), m_keys.lower_bound((tick - len).ticks()));
1317 149 : m_clefs.erase(m_clefs.lower_bound(tick.ticks()), m_clefs.lower_bound((tick - len).ticks()));
1318 : }
1319 :
1320 559 : KeyList kl2;
1321 629 : for (auto i = m_keys.lower_bound(tick.ticks()); i != m_keys.end();) {
1322 70 : KeySigEvent kse = i->second;
1323 70 : Fraction t = Fraction::fromTicks(i->first);
1324 70 : if (t + len < score()->endTick()) {
1325 30 : m_keys.erase(i++);
1326 30 : kl2[(t + len).ticks()] = kse;
1327 : } else {
1328 40 : ++i;
1329 : }
1330 70 : }
1331 559 : m_keys.insert(kl2.begin(), kl2.end());
1332 :
1333 : // check if there is a clef at the end of measure
1334 : // before tick
1335 559 : Clef* clef = 0;
1336 559 : Measure* m = score()->tick2measure(tick);
1337 559 : if (m && (m->tick() == tick) && (m->prevMeasure())) {
1338 328 : m = m->prevMeasure();
1339 328 : Segment* s = m->findSegment(SegmentType::Clef, tick);
1340 328 : if (s) {
1341 3 : track_idx_t track = idx() * VOICES;
1342 3 : clef = toClef(s->element(track));
1343 : }
1344 : }
1345 :
1346 559 : ClefList cl2;
1347 599 : for (auto i = m_clefs.lower_bound(tick.ticks()); i != m_clefs.end();) {
1348 40 : ClefTypeList ctl = i->second;
1349 40 : Fraction t = Fraction::fromTicks(i->first);
1350 40 : if (clef && tick == t) {
1351 2 : ++i;
1352 2 : continue;
1353 : }
1354 38 : m_clefs.erase(i++);
1355 38 : cl2.setClef((t + len).ticks(), ctl);
1356 : }
1357 559 : m_clefs.insert(cl2.begin(), cl2.end());
1358 :
1359 : // check if there is a clef at the end of measure
1360 : // before tick: do not remove from clefs list
1361 :
1362 559 : if (clef) {
1363 3 : setClef(clef);
1364 : }
1365 :
1366 559 : updateOttava();
1367 : DUMP_CLEFS(" insertTime");
1368 559 : }
1369 :
1370 : //---------------------------------------------------------
1371 : // staffList
1372 : // return list of linked staves
1373 : //---------------------------------------------------------
1374 :
1375 5963 : std::vector<Staff*> Staff::staffList() const
1376 : {
1377 5963 : std::vector<Staff*> staffList;
1378 5963 : if (m_links) {
1379 141 : staffList.reserve(m_links->size());
1380 427 : for (EngravingObject* e : *m_links) {
1381 286 : staffList.push_back(toStaff(e));
1382 : }
1383 : } else {
1384 5822 : staffList.push_back(const_cast<Staff*>(this));
1385 : }
1386 5963 : return staffList;
1387 0 : }
1388 :
1389 1485 : Staff* Staff::primaryStaff() const
1390 : {
1391 1485 : const LinkedObjects* linkedElements = links();
1392 1485 : if (!linkedElements) {
1393 357 : return nullptr;
1394 : }
1395 :
1396 1128 : for (auto linkedElement : *linkedElements) {
1397 1128 : Staff* staff = toStaff(linkedElement);
1398 1128 : if (staff && staff->isPrimaryStaff()) {
1399 1128 : return staff;
1400 : }
1401 : }
1402 :
1403 0 : return nullptr;
1404 : }
1405 :
1406 : //---------------------------------------------------------
1407 : // rstaff
1408 : //---------------------------------------------------------
1409 :
1410 308 : staff_idx_t Staff::rstaff() const
1411 : {
1412 308 : return muse::indexOf(m_part->staves(), this);
1413 : }
1414 :
1415 : //---------------------------------------------------------
1416 : // isTop
1417 : //---------------------------------------------------------
1418 :
1419 294 : bool Staff::isTop() const
1420 : {
1421 294 : if (m_part->staves().empty()) {
1422 0 : return false;
1423 : }
1424 :
1425 294 : return m_part->staves().front() == this;
1426 : }
1427 :
1428 : //---------------------------------------------------------
1429 : // getProperty
1430 : //---------------------------------------------------------
1431 :
1432 18110 : PropertyValue Staff::getProperty(Pid id) const
1433 : {
1434 18110 : switch (id) {
1435 0 : case Pid::SMALL:
1436 0 : return staffType(Fraction(0, 1))->isSmall();
1437 0 : case Pid::MAG:
1438 0 : return staffType(Fraction(0, 1))->userMag();
1439 0 : case Pid::LINE_DISTANCE:
1440 0 : return staffType(Fraction(0, 1))->lineDistance();
1441 0 : case Pid::STAFF_INVISIBLE:
1442 0 : return staffType(Fraction(0, 1))->invisible();
1443 0 : case Pid::HIDE_WHEN_EMPTY:
1444 0 : return m_hideWhenEmpty;
1445 0 : case Pid::STAFF_COLOR:
1446 0 : return PropertyValue::fromValue(staffType(Fraction(0, 1))->color());
1447 1969 : case Pid::PLAYBACK_VOICE1:
1448 1969 : return playbackVoice(0);
1449 1969 : case Pid::PLAYBACK_VOICE2:
1450 1969 : return playbackVoice(1);
1451 1969 : case Pid::PLAYBACK_VOICE3:
1452 1969 : return playbackVoice(2);
1453 1969 : case Pid::PLAYBACK_VOICE4:
1454 1969 : return playbackVoice(3);
1455 2224 : case Pid::STAFF_BARLINE_SPAN:
1456 2224 : return barLineSpan();
1457 1970 : case Pid::STAFF_BARLINE_SPAN_FROM:
1458 1970 : return barLineFrom();
1459 1970 : case Pid::STAFF_BARLINE_SPAN_TO:
1460 1970 : return barLineTo();
1461 1969 : case Pid::STAFF_USERDIST:
1462 1969 : return userDist();
1463 69 : case Pid::GENERATED:
1464 69 : return false;
1465 1969 : case Pid::SHOW_MEASURE_NUMBERS:
1466 1969 : return m_showMeasureNumbers;
1467 0 : case Pid::SHOW_IF_ENTIRE_SYSTEM_EMPTY:
1468 0 : return m_showIfEntireSystemEmpty;
1469 27 : case Pid::VISIBLE:
1470 27 : return visible();
1471 9 : case Pid::STAFF_CUTAWAY:
1472 9 : return cutaway();
1473 9 : case Pid::STAFF_HIDE_SYSTEM_BARLINE:
1474 9 : return hideSystemBarLine();
1475 9 : case Pid::STAFF_MERGE_MATCHING_RESTS:
1476 9 : return int(mergeMatchingRests());
1477 9 : case Pid::STAFF_REFLECT_TRANSPOSITION:
1478 9 : return reflectTranspositionInLinkedTab();
1479 0 : default:
1480 0 : LOGD("unhandled id <%s>", propertyName(id));
1481 0 : return PropertyValue();
1482 : }
1483 : }
1484 :
1485 : //---------------------------------------------------------
1486 : // setProperty
1487 : //---------------------------------------------------------
1488 :
1489 226 : bool Staff::setProperty(Pid id, const PropertyValue& v)
1490 : {
1491 226 : switch (id) {
1492 0 : case Pid::SMALL: {
1493 0 : double _spatium = spatium(Fraction(0, 1));
1494 0 : staffType(Fraction(0, 1))->setSmall(v.toBool());
1495 0 : setLocalSpatium(_spatium, spatium(Fraction(0, 1)), Fraction(0, 1));
1496 0 : break;
1497 : }
1498 9 : case Pid::MAG: {
1499 9 : double _spatium = spatium(Fraction(0, 1));
1500 9 : staffType(Fraction(0, 1))->setUserMag(v.toReal());
1501 9 : setLocalSpatium(_spatium, spatium(Fraction(0, 1)), Fraction(0, 1));
1502 9 : break;
1503 : }
1504 9 : case Pid::LINE_DISTANCE: {
1505 9 : staffType(Fraction(0, 1))->setLineDistance(v.value<Spatium>());
1506 9 : break;
1507 : }
1508 0 : case Pid::HIDE_WHEN_EMPTY:
1509 0 : setHideWhenEmpty(v.value<AutoOnOff>());
1510 0 : break;
1511 0 : case Pid::STAFF_COLOR:
1512 0 : setColor(Fraction(0, 1), v.value<Color>());
1513 0 : break;
1514 0 : case Pid::PLAYBACK_VOICE1:
1515 0 : setPlaybackVoice(0, v.toBool());
1516 0 : break;
1517 0 : case Pid::PLAYBACK_VOICE2:
1518 0 : setPlaybackVoice(1, v.toBool());
1519 0 : break;
1520 0 : case Pid::PLAYBACK_VOICE3:
1521 0 : setPlaybackVoice(2, v.toBool());
1522 0 : break;
1523 0 : case Pid::PLAYBACK_VOICE4:
1524 0 : setPlaybackVoice(3, v.toBool());
1525 0 : break;
1526 180 : case Pid::STAFF_BARLINE_SPAN: {
1527 180 : setBarLineSpan(v.toBool());
1528 : // update non-generated barlines
1529 180 : track_idx_t track = idx() * VOICES;
1530 180 : std::vector<EngravingItem*> blList;
1531 1837 : for (Measure* m = score()->firstMeasure(); m; m = m->nextMeasure()) {
1532 1657 : Segment* s = m->getSegmentR(SegmentType::EndBarLine, m->ticks());
1533 1657 : if (s && s->element(track)) {
1534 605 : blList.push_back(s->element(track));
1535 : }
1536 1657 : if (Measure* mm = m->mmRest()) {
1537 0 : Segment* ss = mm->getSegmentR(SegmentType::EndBarLine, mm->ticks());
1538 0 : if (ss && ss->element(track)) {
1539 0 : blList.push_back(ss->element(track));
1540 : }
1541 : }
1542 : }
1543 785 : for (EngravingItem* e : blList) {
1544 605 : if (e && e->isBarLine() && !e->generated()) {
1545 85 : toBarLine(e)->setSpanStaff(barLineSpan());
1546 : }
1547 : }
1548 180 : }
1549 180 : break;
1550 1 : case Pid::STAFF_BARLINE_SPAN_FROM:
1551 1 : setBarLineFrom(v.toInt());
1552 1 : break;
1553 1 : case Pid::STAFF_BARLINE_SPAN_TO:
1554 1 : setBarLineTo(v.toInt());
1555 1 : break;
1556 0 : case Pid::STAFF_USERDIST:
1557 0 : setUserDist(v.value<Spatium>());
1558 0 : break;
1559 0 : case Pid::SHOW_MEASURE_NUMBERS:
1560 0 : m_showMeasureNumbers = v.value<AutoOnOff>();
1561 0 : break;
1562 0 : case Pid::SHOW_IF_ENTIRE_SYSTEM_EMPTY:
1563 0 : m_showIfEntireSystemEmpty = v.toBool();
1564 0 : break;
1565 10 : case Pid::VISIBLE:
1566 10 : setVisible(v.toBool());
1567 10 : masterScore()->rebuildMidiMapping();
1568 10 : break;
1569 4 : case Pid::STAFF_CUTAWAY:
1570 4 : setCutaway(v.toBool());
1571 4 : break;
1572 4 : case Pid::STAFF_HIDE_SYSTEM_BARLINE:
1573 4 : setHideSystemBarLine(v.toBool());
1574 4 : break;
1575 4 : case Pid::STAFF_MERGE_MATCHING_RESTS:
1576 4 : setMergeMatchingRests(AutoOnOff(v.toInt()));
1577 4 : break;
1578 4 : case Pid::STAFF_REFLECT_TRANSPOSITION:
1579 4 : setReflectTranspositionInLinkedTab(v.toBool());
1580 4 : break;
1581 0 : default:
1582 0 : LOGD("unhandled id <%s>", propertyName(id));
1583 0 : break;
1584 : }
1585 226 : triggerLayout();
1586 226 : return true;
1587 : }
1588 :
1589 : //---------------------------------------------------------
1590 : // propertyDefault
1591 : //---------------------------------------------------------
1592 :
1593 17721 : PropertyValue Staff::propertyDefault(Pid id) const
1594 : {
1595 17721 : switch (id) {
1596 0 : case Pid::SMALL:
1597 0 : return false;
1598 0 : case Pid::MAG:
1599 0 : return 1.0;
1600 0 : case Pid::HIDE_WHEN_EMPTY:
1601 0 : return AutoOnOff::AUTO;
1602 0 : case Pid::STAFF_COLOR:
1603 0 : return PropertyValue::fromValue(configuration()->defaultColor());
1604 7876 : case Pid::PLAYBACK_VOICE1:
1605 : case Pid::PLAYBACK_VOICE2:
1606 : case Pid::PLAYBACK_VOICE3:
1607 : case Pid::PLAYBACK_VOICE4:
1608 7876 : return true;
1609 1969 : case Pid::STAFF_BARLINE_SPAN:
1610 1969 : return false;
1611 3938 : case Pid::STAFF_BARLINE_SPAN_FROM:
1612 : case Pid::STAFF_BARLINE_SPAN_TO:
1613 3938 : return 0;
1614 1969 : case Pid::STAFF_USERDIST:
1615 1969 : return 0.0_sp;
1616 1969 : case Pid::SHOW_MEASURE_NUMBERS:
1617 3938 : return AutoOnOff::AUTO;
1618 0 : case Pid::SHOW_IF_ENTIRE_SYSTEM_EMPTY:
1619 0 : return false;
1620 0 : case Pid::VISIBLE:
1621 0 : return true;
1622 0 : case Pid::STAFF_CUTAWAY:
1623 0 : return false;
1624 0 : case Pid::STAFF_HIDE_SYSTEM_BARLINE:
1625 0 : return false;
1626 0 : case Pid::STAFF_MERGE_MATCHING_RESTS:
1627 0 : return int(AutoOnOff::AUTO);
1628 0 : case Pid::STAFF_REFLECT_TRANSPOSITION:
1629 0 : return true;
1630 0 : default:
1631 0 : LOGD("unhandled id <%s>", propertyName(id));
1632 0 : return PropertyValue();
1633 : }
1634 : }
1635 :
1636 : //---------------------------------------------------------
1637 : // localSpatiumChanged
1638 : //---------------------------------------------------------
1639 :
1640 9 : void Staff::setLocalSpatium(double oldVal, double newVal, Fraction tick)
1641 : {
1642 9 : const int intEndTick = m_staffTypeList.staffTypeRange(tick).second;
1643 9 : const Fraction etick = (intEndTick == -1) ? score()->lastMeasure()->endTick() : Fraction::fromTicks(intEndTick);
1644 :
1645 9 : staff_idx_t staffIdx = idx();
1646 9 : track_idx_t startTrack = staffIdx * VOICES;
1647 9 : track_idx_t endTrack = startTrack + VOICES;
1648 10 : for (Segment* s = score()->tick2rightSegment(tick); s && s->tick() < etick; s = s->next1()) {
1649 1 : for (EngravingItem* e : s->annotations()) {
1650 0 : if (e->track() >= startTrack && e->track() < endTrack) {
1651 0 : e->localSpatiumChanged(oldVal, newVal);
1652 : }
1653 : }
1654 5 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
1655 4 : if (s->element(track)) {
1656 0 : s->element(track)->localSpatiumChanged(oldVal, newVal);
1657 : }
1658 : }
1659 : }
1660 9 : auto spanners = score()->spannerMap().findContained(tick.ticks(), etick.ticks());
1661 9 : for (auto interval : spanners) {
1662 0 : Spanner* spanner = interval.value;
1663 0 : if (spanner->staffIdx() == staffIdx) {
1664 0 : for (auto k : spanner->spannerSegments()) {
1665 0 : k->localSpatiumChanged(oldVal, newVal);
1666 : }
1667 : }
1668 : }
1669 9 : }
1670 :
1671 : //---------------------------------------------------------
1672 : // isPitchedStaff
1673 : //---------------------------------------------------------
1674 :
1675 64318 : bool Staff::isPitchedStaff(const Fraction& tick) const
1676 : {
1677 : //return staffType(tick)->group() == StaffGroup::STANDARD;
1678 64318 : return staffType(tick)->group() != StaffGroup::TAB && !originPart(tick)->instrument(tick)->useDrumset();
1679 : }
1680 :
1681 : //---------------------------------------------------------
1682 : // isTabStaff
1683 : //---------------------------------------------------------
1684 :
1685 834514 : bool Staff::isTabStaff(const Fraction& tick) const
1686 : {
1687 834514 : return staffType(tick)->group() == StaffGroup::TAB;
1688 : }
1689 :
1690 : //---------------------------------------------------------
1691 : // isDrumStaff
1692 : //---------------------------------------------------------
1693 :
1694 40538 : bool Staff::isDrumStaff(const Fraction& tick) const
1695 : {
1696 : //check for instrument instead of staffType (for pitched to unpitched instr. changes)
1697 40538 : return originPart(tick)->instrument(tick)->useDrumset();
1698 : }
1699 :
1700 : //---------------------------------------------------------
1701 : // lines
1702 : //---------------------------------------------------------
1703 :
1704 547239 : int Staff::lines(const Fraction& tick) const
1705 : {
1706 547239 : return staffType(tick)->lines();
1707 : }
1708 :
1709 : //---------------------------------------------------------
1710 : // setLines
1711 : //---------------------------------------------------------
1712 :
1713 761 : void Staff::setLines(const Fraction& tick, int val)
1714 : {
1715 761 : staffType(tick)->setLines(val);
1716 761 : }
1717 :
1718 : //---------------------------------------------------------
1719 : // lineDistance
1720 : // distance between staff lines
1721 : //---------------------------------------------------------
1722 :
1723 339289 : double Staff::lineDistance(const Fraction& tick) const
1724 : {
1725 339289 : return staffType(tick)->lineDistance().val();
1726 : }
1727 :
1728 : //---------------------------------------------------------
1729 : // invisible
1730 : //---------------------------------------------------------
1731 :
1732 119209 : bool Staff::isLinesInvisible(const Fraction& tick) const
1733 : {
1734 119209 : return staffType(tick)->invisible();
1735 : }
1736 :
1737 : //---------------------------------------------------------
1738 : // setInvisible
1739 : //---------------------------------------------------------
1740 :
1741 0 : void Staff::setIsLinesInvisible(const Fraction& tick, bool val)
1742 : {
1743 0 : staffType(tick)->setInvisible(val);
1744 0 : }
1745 : }
|