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 "input.h"
24 :
25 : #include "articulation.h"
26 : #include "chord.h"
27 : #include "durationtype.h"
28 : #include "hook.h"
29 : #include "measure.h"
30 : #include "note.h"
31 : #include "part.h"
32 : #include "rest.h"
33 : #include "score.h"
34 : #include "segment.h"
35 : #include "select.h"
36 : #include "staff.h"
37 : #include "stem.h"
38 : #include "tuplet.h"
39 :
40 : using namespace mu;
41 :
42 : namespace mu::engraving {
43 : class DrumSet;
44 :
45 5 : bool InputState::isValid() const
46 : {
47 5 : return m_segment != nullptr && m_track != muse::nidx;
48 : }
49 :
50 : //---------------------------------------------------------
51 : // drumset
52 : //---------------------------------------------------------
53 :
54 0 : Staff* InputState::staff() const
55 : {
56 0 : if (!m_segment || m_track == muse::nidx) {
57 0 : return nullptr;
58 : }
59 :
60 0 : return m_segment->score()->staff(mu::engraving::track2staff(m_track));
61 : }
62 :
63 0 : staff_idx_t InputState::staffIdx() const
64 : {
65 0 : return mu::engraving::track2staff(m_track);
66 : }
67 :
68 75 : Drumset* InputState::drumset() const
69 : {
70 75 : if (!m_segment || m_track == muse::nidx) {
71 0 : return nullptr;
72 : }
73 :
74 75 : const Staff* staff = m_segment->score()->staff(m_track / VOICES);
75 75 : if (!staff) {
76 0 : return nullptr;
77 : }
78 :
79 75 : return staff->part()->instrument(m_segment->tick())->drumset();
80 : }
81 :
82 : //---------------------------------------------------------
83 : // staffGroup
84 : //---------------------------------------------------------
85 :
86 0 : StaffGroup InputState::staffGroup() const
87 : {
88 0 : if (!m_segment || m_track == muse::nidx) {
89 0 : return StaffGroup::STANDARD;
90 : }
91 :
92 0 : Fraction tick = m_segment->tick();
93 0 : const Staff* staff = m_segment->score()->staff(m_track / VOICES);
94 0 : if (!staff) {
95 0 : return StaffGroup::STANDARD;
96 : }
97 :
98 0 : StaffGroup staffGroup = staff->staffType(tick)->group();
99 0 : const Instrument* instrument = staff->part()->instrument(tick);
100 :
101 : // if not tab, pitched/unpitched input depends on instrument, not staff (override StaffGroup)
102 0 : if (staffGroup != StaffGroup::TAB) {
103 0 : staffGroup = instrument->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
104 : }
105 :
106 0 : return staffGroup;
107 : }
108 :
109 : //---------------------------------------------------------
110 : // tick
111 : //---------------------------------------------------------
112 :
113 0 : Fraction InputState::tick() const
114 : {
115 0 : return m_segment ? m_segment->tick() : Fraction(0, 1);
116 : }
117 :
118 : //---------------------------------------------------------
119 : // cr
120 : //---------------------------------------------------------
121 :
122 171 : ChordRest* InputState::cr() const
123 : {
124 : // _track could potentially be invalid, for instance after navigation through a frame
125 171 : return m_segment && m_track != muse::nidx ? toChordRest(m_segment->element(m_track)) : 0;
126 : }
127 :
128 : //---------------------------------------------------------
129 : // setDots
130 : //---------------------------------------------------------
131 :
132 0 : void InputState::setDots(int n)
133 : {
134 0 : if (n && (!m_duration.isValid() || m_duration.isZero() || m_duration.isMeasure())) {
135 0 : m_duration = DurationType::V_QUARTER;
136 : }
137 0 : m_duration.setDots(n);
138 0 : }
139 :
140 8 : void InputState::setVoice(voice_idx_t v)
141 : {
142 8 : const Score* score = m_segment ? m_segment->score() : nullptr;
143 :
144 8 : if (!score || v >= VOICES || v == voice() || m_track == muse::nidx) {
145 0 : return;
146 : }
147 :
148 8 : const track_idx_t newTrack = (m_track / VOICES) * VOICES + v;
149 8 : Segment* currSeg = segment();
150 8 : if (!currSeg || currSeg->cr(newTrack)) {
151 1 : setTrack(newTrack);
152 1 : return;
153 : }
154 :
155 : // If we haven't returned early - it means that currSeg doesn't have a valid ChordRest for the desired voice. If
156 : // tuplets are involved, we may need to move the input position backwards to the last valid ChordRest...
157 7 : Segment* candidateSeg = currSeg;
158 :
159 : // First thing to check - is currSeg part of a tuplet? If so, move candidateSeg to the start of that tuplet...
160 7 : const ChordRest* currCR = currSeg->cr(track());
161 7 : const Tuplet* currTuplet = currCR ? currCR->topTuplet() : nullptr;
162 7 : if (currTuplet) {
163 5 : candidateSeg = score->tick2segment(currTuplet->tick());
164 5 : IF_ASSERT_FAILED(candidateSeg) {
165 0 : setSegment(currSeg->measure()->first());
166 0 : setTrack(newTrack);
167 0 : return;
168 : }
169 : }
170 :
171 : // If candidateSeg still doesn't have a valid ChordRest for the desired voice, we'll now check whether it is
172 : // within the range of a tuplet at our desired voice. If so, move candidateSeg to the start of that tuplet...
173 7 : const ChordRest* nextCR = candidateSeg->nextChordRest(newTrack, /*backwards*/ true, /*stopAtMeasureBoundary*/ true);
174 7 : const Tuplet* nextTuplet = nextCR ? nextCR->topTuplet() : nullptr;
175 7 : if (!candidateSeg->cr(newTrack) && nextTuplet) {
176 1 : candidateSeg = score->tick2segment(nextTuplet->tick());
177 1 : IF_ASSERT_FAILED(candidateSeg) {
178 0 : setSegment(currSeg->measure()->first());
179 0 : setTrack(newTrack);
180 0 : return;
181 : }
182 : }
183 :
184 7 : if (candidateSeg != currSeg) {
185 6 : setSegment(candidateSeg);
186 : }
187 :
188 7 : setTrack(newTrack);
189 : }
190 :
191 : //---------------------------------------------------------
192 : // note
193 : //---------------------------------------------------------
194 :
195 11 : Note* InputState::note(EngravingItem* e)
196 : {
197 11 : return e && e->isNote() ? toNote(e) : nullptr;
198 : }
199 :
200 : //---------------------------------------------------------
201 : // chordRest
202 : //---------------------------------------------------------
203 :
204 1 : ChordRest* InputState::chordRest(EngravingItem* e)
205 : {
206 1 : if (!e) {
207 1 : return nullptr;
208 : }
209 0 : if (e->isChordRest()) {
210 0 : return toChordRest(e);
211 : }
212 0 : if (e->isNote()) {
213 0 : return toNote(e)->chord();
214 : }
215 0 : if (e->isStem()) {
216 0 : return toStem(e)->chord();
217 : }
218 0 : if (e->isHook()) {
219 0 : return toHook(e)->chord();
220 : }
221 0 : return nullptr;
222 : }
223 :
224 : //---------------------------------------------------------
225 : // update
226 : //---------------------------------------------------------
227 :
228 0 : void InputState::update(Selection& selection)
229 : {
230 0 : setDuration(DurationType::V_INVALID);
231 0 : setRest(false);
232 0 : setAccidentalType(AccidentalType::NONE);
233 0 : setNotes({});
234 0 : setBeyondScore(false);
235 :
236 0 : Note* n1 = nullptr;
237 0 : ChordRest* cr1 = nullptr;
238 0 : bool differentAccidentals = false;
239 0 : bool differentDurations = false;
240 0 : bool chordsAndRests = false;
241 :
242 0 : std::set<SymId> articulationSymbolIds;
243 0 : for (EngravingItem* e : selection.elements()) {
244 0 : if (Note* n = note(e)) {
245 0 : if (n1) {
246 0 : if (n->accidentalType() != n1->accidentalType()) {
247 0 : setAccidentalType(AccidentalType::NONE);
248 0 : differentAccidentals = true;
249 : }
250 :
251 0 : std::set<SymId> articulationsIds;
252 0 : for (Articulation* artic: n->chord()->articulations()) {
253 0 : articulationsIds.insert(artic->symId());
254 : }
255 :
256 0 : articulationsIds = mu::engraving::splitArticulations(articulationsIds);
257 0 : articulationsIds = mu::engraving::flipArticulations(articulationsIds, PlacementV::ABOVE);
258 0 : for (const SymId& articulationSymbolId: articulationsIds) {
259 0 : if (std::find(articulationSymbolIds.begin(), articulationSymbolIds.end(),
260 0 : articulationSymbolId) == articulationSymbolIds.end()) {
261 0 : articulationSymbolIds.erase(articulationSymbolId);
262 : }
263 : }
264 0 : } else {
265 0 : setAccidentalType(n->accidentalType());
266 :
267 0 : std::set<SymId> articulationsIds;
268 0 : for (Articulation* artic: n->chord()->articulations()) {
269 0 : if (!artic->isOrnament()) {
270 0 : articulationsIds.insert(artic->symId());
271 : }
272 : }
273 0 : articulationSymbolIds = mu::engraving::flipArticulations(articulationsIds, PlacementV::ABOVE);
274 :
275 0 : n1 = n;
276 0 : }
277 : }
278 :
279 0 : if (ChordRest* cr = chordRest(e)) {
280 0 : if (cr1) {
281 0 : if (cr->durationType() != cr1->durationType()) {
282 0 : setDuration(DurationType::V_INVALID);
283 0 : differentDurations = true;
284 : }
285 0 : if ((cr->isRest() && !cr1->isRest()) || (!cr->isRest() && cr1->isRest())) {
286 0 : setRest(false);
287 0 : chordsAndRests = true;
288 : }
289 : } else {
290 0 : setDuration(cr->durationType());
291 0 : setRest(cr->isRest());
292 0 : cr1 = cr;
293 : }
294 : }
295 :
296 0 : if (differentAccidentals && differentDurations && chordsAndRests) {
297 0 : break;
298 : }
299 : }
300 :
301 0 : setArticulationIds(joinArticulations(articulationSymbolIds));
302 :
303 0 : EngravingItem* e = selection.element();
304 0 : if (!e) {
305 0 : if (!selection.isNone()) {
306 0 : setTrack(selection.activeTrack());
307 0 : setSegment(selection.startSegment());
308 : }
309 0 : return;
310 : }
311 :
312 0 : ChordRest* cr = chordRest(e);
313 0 : Note* n = note(e);
314 0 : if (!n && cr && cr->isChord()) {
315 0 : n = toChord(cr)->upNote();
316 : }
317 :
318 0 : if (cr) {
319 0 : setTrack(cr->track());
320 0 : setNoteType(n ? n->noteType() : NoteType::NORMAL);
321 0 : setBeamMode(cr->beamMode());
322 : }
323 :
324 0 : setDrumNote(-1);
325 0 : if (n) {
326 0 : const Instrument* instr = n->part()->instrument(n->tick());
327 0 : if (instr->useDrumset()) {
328 0 : setDrumNote(n->pitch());
329 : }
330 : }
331 0 : }
332 :
333 : //---------------------------------------------------------
334 : // moveInputPos
335 : //---------------------------------------------------------
336 :
337 0 : void InputState::moveInputPos(EngravingItem* e)
338 : {
339 0 : if (e == 0) {
340 0 : return;
341 : }
342 :
343 : Segment* s;
344 0 : if (e->isChordRest()) {
345 0 : s = toChordRest(e)->segment();
346 : } else {
347 0 : s = toSegment(e);
348 : }
349 :
350 0 : if (s->isSegment()) {
351 0 : if (s->measure()->isMMRest()) {
352 0 : Measure* m = s->measure()->mmRestFirst();
353 0 : s = m->findSegment(SegmentType::ChordRest, m->tick());
354 : }
355 0 : m_lastSegment = m_segment;
356 0 : m_segment = s;
357 : }
358 : }
359 :
360 : //---------------------------------------------------------
361 : // setSegment
362 : //---------------------------------------------------------
363 :
364 1554 : void InputState::setSegment(Segment* s)
365 : {
366 1554 : if (s && s->measure()->isMMRest()) {
367 0 : Measure* m = s->measure()->mmRestFirst();
368 0 : s = m->findSegment(SegmentType::ChordRest, m->tick());
369 : }
370 1554 : m_segment = s;
371 1554 : m_lastSegment = s;
372 1554 : }
373 :
374 : //---------------------------------------------------------
375 : // nextInputPos
376 : //---------------------------------------------------------
377 :
378 141 : Segment* InputState::nextInputPos() const
379 : {
380 141 : Measure* m = m_segment->measure();
381 141 : Segment* s = m_segment->next1(SegmentType::ChordRest);
382 146 : while (s) {
383 139 : if (s->element(m_track) || s->measure() != m) {
384 134 : return s;
385 : }
386 5 : s = s->next1(SegmentType::ChordRest);
387 : }
388 7 : return 0;
389 : }
390 :
391 : //---------------------------------------------------------
392 : // moveToNextInputPos
393 : // TODO: special case: note is first note of tie: goto to last note of tie
394 : //---------------------------------------------------------
395 :
396 141 : void InputState::moveToNextInputPos()
397 : {
398 141 : Segment* s = nextInputPos();
399 141 : m_lastSegment = m_segment;
400 141 : if (s) {
401 134 : m_segment = s;
402 134 : m_beyondScore = false;
403 : } else {
404 7 : m_beyondScore = true;
405 : }
406 141 : }
407 : }
|