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 : /**
24 : \file
25 : Implementation of classes Note and ShadowNote.
26 : */
27 :
28 : #include "note.h"
29 :
30 : #include <cassert>
31 :
32 : #include "translation.h"
33 :
34 : #include "iengravingconfiguration.h" // IWYU pragma: keep
35 : #include "iengravingfont.h"
36 :
37 : #include "../editing/addremoveelement.h"
38 : #include "../editing/editchord.h"
39 : #include "../editing/editnote.h"
40 : #include "../editing/editparentheses.h"
41 : #include "../editing/noteinput.h"
42 : #include "../editing/transaction/transaction.h"
43 : #include "../editing/transpose.h"
44 : #include "types/typesconv.h"
45 :
46 : #include "rendering/score/horizontalspacing.h"
47 :
48 : #include "accidental.h"
49 : #include "actionicon.h"
50 : #include "articulation.h"
51 : #include "bagpembell.h"
52 : #include "barline.h"
53 : #include "beam.h"
54 : #include "chord.h"
55 : #include "chordline.h"
56 : #include "drumset.h"
57 : #include "factory.h"
58 : #include "fingering.h"
59 : #include "glissando.h"
60 : #include "guitarbend.h"
61 : #include "laissezvib.h"
62 : #include "linkedobjects.h"
63 : #include "measure.h"
64 : #include "notedot.h"
65 : #include "noteline.h"
66 : #include "parenthesis.h"
67 : #include "part.h"
68 : #include "partialtie.h"
69 : #include "pitchspelling.h"
70 : #include "score.h"
71 : #include "segment.h"
72 : #include "spanner.h"
73 : #include "staff.h"
74 : #include "stafftype.h"
75 : #include "stringdata.h"
76 : #include "tie.h"
77 : #include "utils.h"
78 : #include "volta.h"
79 :
80 : #include "log.h"
81 :
82 : using namespace mu;
83 : using namespace muse::draw;
84 : using namespace mu::engraving;
85 : using namespace mu::engraving::rendering::score;
86 :
87 : namespace mu::engraving {
88 : //---------------------------------------------------------
89 : // noteHeads
90 : // notehead groups
91 : //---------------------------------------------------------
92 :
93 : //int(NoteHeadGroup::HEAD_GROUPS) - 1: "-1" is needed to prevent building CUSTOM_GROUP noteheads set, since it is built by users and keep a specific set of existing noteheads
94 : static const SymId noteHeads[2][int(NoteHeadGroup::HEAD_GROUPS) - 1][int(NoteHeadType::HEAD_TYPES)] = {
95 : { // down stem
96 : { SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack,
97 : SymId::noteheadDoubleWhole },
98 : { SymId::noteheadXWhole, SymId::noteheadXHalf, SymId::noteheadXBlack,
99 : SymId::noteheadXDoubleWhole },
100 : { SymId::noteheadPlusWhole, SymId::noteheadPlusHalf, SymId::noteheadPlusBlack,
101 : SymId::noteheadPlusDoubleWhole },
102 : { SymId::noteheadCircleXWhole, SymId::noteheadCircleXHalf, SymId::noteheadCircleX,
103 : SymId::noteheadCircleXDoubleWhole },
104 : { SymId::noteheadWholeWithX, SymId::noteheadHalfWithX, SymId::noteheadVoidWithX,
105 : SymId::noteheadDoubleWholeWithX },
106 : { SymId::noteheadTriangleUpWhole, SymId::noteheadTriangleUpHalf, SymId::noteheadTriangleUpBlack,
107 : SymId::noteheadTriangleUpDoubleWhole },
108 : { SymId::noteheadTriangleDownWhole, SymId::noteheadTriangleDownHalf, SymId::noteheadTriangleDownBlack,
109 : SymId::noteheadTriangleDownDoubleWhole },
110 : { SymId::noteheadSlashedWhole1, SymId::noteheadSlashedHalf1, SymId::noteheadSlashedBlack1,
111 : SymId::noteheadSlashedDoubleWhole1 },
112 : { SymId::noteheadSlashedWhole2, SymId::noteheadSlashedHalf2, SymId::noteheadSlashedBlack2,
113 : SymId::noteheadSlashedDoubleWhole2 },
114 : { SymId::noteheadDiamondWhole, SymId::noteheadDiamondHalf, SymId::noteheadDiamondBlack,
115 : SymId::noteheadDiamondDoubleWhole },
116 : { SymId::noteheadDiamondWholeOld, SymId::noteheadDiamondHalfOld, SymId::noteheadDiamondBlackOld,
117 : SymId::noteheadDiamondDoubleWholeOld },
118 : { SymId::noteheadCircledWhole, SymId::noteheadCircledHalf, SymId::noteheadCircledBlack,
119 : SymId::noteheadCircledDoubleWhole },
120 : { SymId::noteheadCircledWholeLarge, SymId::noteheadCircledHalfLarge, SymId::noteheadCircledBlackLarge,
121 : SymId::noteheadCircledDoubleWholeLarge },
122 : { SymId::noteheadLargeArrowUpWhole, SymId::noteheadLargeArrowUpHalf, SymId::noteheadLargeArrowUpBlack,
123 : SymId::noteheadLargeArrowUpDoubleWhole },
124 : { SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack,
125 : SymId::noteheadDoubleWholeSquare },
126 :
127 : { SymId::noteheadSlashWhiteWhole, SymId::noteheadSlashWhiteHalf, SymId::noteheadSlashHorizontalEnds,
128 : SymId::noteheadSlashWhiteWhole },
129 : { SymId::noteheadSlashDiamondWhite, SymId::noteheadSlashDiamondWhite, SymId::noteheadSlashHorizontalEnds,
130 : SymId::noteheadSlashWhiteWhole },
131 :
132 : { SymId::noteShapeRoundWhite, SymId::noteShapeRoundWhite, SymId::noteShapeRoundBlack,
133 : SymId::noteShapeRoundDoubleWhole },
134 : { SymId::noteShapeSquareWhite, SymId::noteShapeSquareWhite, SymId::noteShapeSquareBlack,
135 : SymId::noteShapeSquareDoubleWhole },
136 : { SymId::noteShapeTriangleRightWhite, SymId::noteShapeTriangleRightWhite, SymId::noteShapeTriangleRightBlack,
137 : SymId::noteShapeTriangleRightDoubleWhole },
138 : { SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondBlack,
139 : SymId::noteShapeDiamondDoubleWhole },
140 : { SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpBlack,
141 : SymId::noteShapeTriangleUpDoubleWhole },
142 : { SymId::noteShapeMoonWhite, SymId::noteShapeMoonWhite, SymId::noteShapeMoonBlack,
143 : SymId::noteShapeMoonDoubleWhole },
144 : { SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundBlack,
145 : SymId::noteShapeTriangleRoundDoubleWhole },
146 :
147 : { SymId::noteheadHeavyX, SymId::noteheadHeavyX, SymId::noteheadHeavyX,
148 : SymId::noteheadHeavyX },
149 : { SymId::noteheadHeavyXHat, SymId::noteheadHeavyXHat, SymId::noteheadHeavyXHat,
150 : SymId::noteheadHeavyXHat },
151 :
152 : { SymId::noteShapeKeystoneWhite, SymId::noteShapeKeystoneWhite, SymId::noteShapeKeystoneBlack,
153 : SymId::noteShapeKeystoneDoubleWhole },
154 : { SymId::noteShapeQuarterMoonWhite, SymId::noteShapeQuarterMoonWhite, SymId::noteShapeQuarterMoonBlack,
155 : SymId::noteShapeQuarterMoonDoubleWhole },
156 : { SymId::noteShapeIsoscelesTriangleWhite, SymId::noteShapeIsoscelesTriangleWhite, SymId::noteShapeIsoscelesTriangleBlack,
157 : SymId::noteShapeIsoscelesTriangleDoubleWhole },
158 : { SymId::noteShapeMoonLeftWhite, SymId::noteShapeMoonLeftWhite, SymId::noteShapeMoonLeftBlack,
159 : SymId::noteShapeMoonLeftDoubleWhole },
160 : { SymId::noteShapeArrowheadLeftWhite, SymId::noteShapeArrowheadLeftWhite, SymId::noteShapeArrowheadLeftBlack,
161 : SymId::noteShapeArrowheadLeftDoubleWhole },
162 : { SymId::noteShapeTriangleRoundLeftWhite, SymId::noteShapeTriangleRoundLeftWhite, SymId::noteShapeTriangleRoundLeftBlack,
163 : SymId::noteShapeTriangleRoundLeftDoubleWhole },
164 :
165 : { SymId::noteDoWhole, SymId::noteDoHalf, SymId::noteDoBlack, SymId::noSym },
166 : { SymId::noteDiWhole, SymId::noteDiHalf, SymId::noteDiBlack, SymId::noSym },
167 : { SymId::noteRaWhole, SymId::noteRaHalf, SymId::noteRaBlack, SymId::noSym },
168 : { SymId::noteReWhole, SymId::noteReHalf, SymId::noteReBlack, SymId::noSym },
169 : { SymId::noteRiWhole, SymId::noteRiHalf, SymId::noteRiBlack, SymId::noSym },
170 : { SymId::noteMeWhole, SymId::noteMeHalf, SymId::noteMeBlack, SymId::noSym },
171 : { SymId::noteMiWhole, SymId::noteMiHalf, SymId::noteMiBlack, SymId::noSym },
172 : { SymId::noteFaWhole, SymId::noteFaHalf, SymId::noteFaBlack, SymId::noSym },
173 : { SymId::noteFiWhole, SymId::noteFiHalf, SymId::noteFiBlack, SymId::noSym },
174 : { SymId::noteSeWhole, SymId::noteSeHalf, SymId::noteSeBlack, SymId::noSym },
175 : { SymId::noteSoWhole, SymId::noteSoHalf, SymId::noteSoBlack, SymId::noSym },
176 : { SymId::noteLeWhole, SymId::noteLeHalf, SymId::noteLeBlack, SymId::noSym },
177 : { SymId::noteLaWhole, SymId::noteLaHalf, SymId::noteLaBlack, SymId::noSym },
178 : { SymId::noteLiWhole, SymId::noteLiHalf, SymId::noteLiBlack, SymId::noSym },
179 : { SymId::noteTeWhole, SymId::noteTeHalf, SymId::noteTeBlack, SymId::noSym },
180 : { SymId::noteTiWhole, SymId::noteTiHalf, SymId::noteTiBlack, SymId::noSym },
181 : { SymId::noteSiWhole, SymId::noteSiHalf, SymId::noteSiBlack, SymId::noSym },
182 :
183 : { SymId::noteASharpWhole, SymId::noteASharpHalf, SymId::noteASharpBlack, SymId::noSym },
184 : { SymId::noteAWhole, SymId::noteAHalf, SymId::noteABlack, SymId::noSym },
185 : { SymId::noteAFlatWhole, SymId::noteAFlatHalf, SymId::noteAFlatBlack, SymId::noSym },
186 : { SymId::noteBSharpWhole, SymId::noteBSharpHalf, SymId::noteBSharpBlack, SymId::noSym },
187 : { SymId::noteBWhole, SymId::noteBHalf, SymId::noteBBlack, SymId::noSym },
188 : { SymId::noteBFlatWhole, SymId::noteBFlatHalf, SymId::noteBFlatBlack, SymId::noSym },
189 : { SymId::noteCSharpWhole, SymId::noteCSharpHalf, SymId::noteCSharpBlack, SymId::noSym },
190 : { SymId::noteCWhole, SymId::noteCHalf, SymId::noteCBlack, SymId::noSym },
191 : { SymId::noteCFlatWhole, SymId::noteCFlatHalf, SymId::noteCFlatBlack, SymId::noSym },
192 : { SymId::noteDSharpWhole, SymId::noteDSharpHalf, SymId::noteDSharpBlack, SymId::noSym },
193 : { SymId::noteDWhole, SymId::noteDHalf, SymId::noteDBlack, SymId::noSym },
194 : { SymId::noteDFlatWhole, SymId::noteDFlatHalf, SymId::noteDFlatBlack, SymId::noSym },
195 : { SymId::noteESharpWhole, SymId::noteESharpHalf, SymId::noteESharpBlack, SymId::noSym },
196 : { SymId::noteEWhole, SymId::noteEHalf, SymId::noteEBlack, SymId::noSym },
197 : { SymId::noteEFlatWhole, SymId::noteEFlatHalf, SymId::noteEFlatBlack, SymId::noSym },
198 : { SymId::noteFSharpWhole, SymId::noteFSharpHalf, SymId::noteFSharpBlack, SymId::noSym },
199 : { SymId::noteFWhole, SymId::noteFHalf, SymId::noteFBlack, SymId::noSym },
200 : { SymId::noteFFlatWhole, SymId::noteFFlatHalf, SymId::noteFFlatBlack, SymId::noSym },
201 : { SymId::noteGSharpWhole, SymId::noteGSharpHalf, SymId::noteGSharpBlack, SymId::noSym },
202 : { SymId::noteGWhole, SymId::noteGHalf, SymId::noteGBlack, SymId::noSym },
203 : { SymId::noteGFlatWhole, SymId::noteGFlatHalf, SymId::noteGFlatBlack, SymId::noSym },
204 : { SymId::noteHWhole, SymId::noteHHalf, SymId::noteHBlack, SymId::noSym },
205 : { SymId::noteHSharpWhole, SymId::noteHSharpHalf, SymId::noteHSharpBlack, SymId::noSym },
206 :
207 : { SymId::noSym, SymId::swissRudimentsNoteheadHalfFlam, SymId::swissRudimentsNoteheadBlackFlam, SymId::noSym },
208 : { SymId::noSym, SymId::swissRudimentsNoteheadHalfDouble, SymId::swissRudimentsNoteheadBlackDouble, SymId::noSym }
209 : },
210 : { // up stem
211 : { SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack,
212 : SymId::noteheadDoubleWhole },
213 : { SymId::noteheadXWhole, SymId::noteheadXHalf, SymId::noteheadXBlack,
214 : SymId::noteheadXDoubleWhole },
215 : { SymId::noteheadPlusWhole, SymId::noteheadPlusHalf, SymId::noteheadPlusBlack,
216 : SymId::noteheadPlusDoubleWhole },
217 : { SymId::noteheadCircleXWhole, SymId::noteheadCircleXHalf, SymId::noteheadCircleX,
218 : SymId::noteheadCircleXDoubleWhole },
219 : { SymId::noteheadWholeWithX, SymId::noteheadHalfWithX, SymId::noteheadVoidWithX,
220 : SymId::noteheadDoubleWholeWithX },
221 : { SymId::noteheadTriangleUpWhole, SymId::noteheadTriangleUpHalf, SymId::noteheadTriangleUpBlack,
222 : SymId::noteheadTriangleUpDoubleWhole },
223 : { SymId::noteheadTriangleDownWhole, SymId::noteheadTriangleDownHalf, SymId::noteheadTriangleDownBlack,
224 : SymId::noteheadTriangleDownDoubleWhole },
225 : { SymId::noteheadSlashedWhole1, SymId::noteheadSlashedHalf1, SymId::noteheadSlashedBlack1,
226 : SymId::noteheadSlashedDoubleWhole1 },
227 : { SymId::noteheadSlashedWhole2, SymId::noteheadSlashedHalf2, SymId::noteheadSlashedBlack2,
228 : SymId::noteheadSlashedDoubleWhole2 },
229 : { SymId::noteheadDiamondWhole, SymId::noteheadDiamondHalf, SymId::noteheadDiamondBlack,
230 : SymId::noteheadDiamondDoubleWhole },
231 : { SymId::noteheadDiamondWholeOld, SymId::noteheadDiamondHalfOld, SymId::noteheadDiamondBlackOld,
232 : SymId::noteheadDiamondDoubleWholeOld },
233 : { SymId::noteheadCircledWhole, SymId::noteheadCircledHalf, SymId::noteheadCircledBlack,
234 : SymId::noteheadCircledDoubleWhole },
235 : { SymId::noteheadCircledWholeLarge, SymId::noteheadCircledHalfLarge, SymId::noteheadCircledBlackLarge,
236 : SymId::noteheadCircledDoubleWholeLarge },
237 : // different from down, find source?
238 : { SymId::noteheadLargeArrowDownWhole, SymId::noteheadLargeArrowDownHalf, SymId::noteheadLargeArrowDownBlack,
239 : SymId::noteheadLargeArrowDownDoubleWhole },
240 : { SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack,
241 : SymId::noteheadDoubleWholeSquare },
242 :
243 : { SymId::noteheadSlashWhiteWhole, SymId::noteheadSlashWhiteHalf, SymId::noteheadSlashHorizontalEnds,
244 : SymId::noteheadSlashWhiteDoubleWhole },
245 : { SymId::noteheadSlashDiamondWhite, SymId::noteheadSlashDiamondWhite, SymId::noteheadSlashHorizontalEnds,
246 : SymId::noteheadSlashWhiteDoubleWhole },
247 :
248 : { SymId::noteShapeRoundWhite, SymId::noteShapeRoundWhite, SymId::noteShapeRoundBlack,
249 : SymId::noteShapeRoundDoubleWhole },
250 : { SymId::noteShapeSquareWhite, SymId::noteShapeSquareWhite, SymId::noteShapeSquareBlack,
251 : SymId::noteShapeSquareDoubleWhole },
252 : // different from down
253 : { SymId::noteShapeTriangleLeftWhite, SymId::noteShapeTriangleLeftWhite, SymId::noteShapeTriangleLeftBlack,
254 : SymId::noteShapeTriangleLeftDoubleWhole },
255 : { SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondBlack,
256 : SymId::noteShapeDiamondDoubleWhole },
257 : { SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpBlack,
258 : SymId::noteShapeTriangleUpDoubleWhole },
259 : { SymId::noteShapeMoonWhite, SymId::noteShapeMoonWhite, SymId::noteShapeMoonBlack,
260 : SymId::noteShapeMoonDoubleWhole },
261 : { SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundBlack,
262 : SymId::noteShapeTriangleRoundDoubleWhole },
263 :
264 : { SymId::noteheadHeavyX, SymId::noteheadHeavyX, SymId::noteheadHeavyX,
265 : SymId::noteheadHeavyX },
266 : { SymId::noteheadHeavyXHat, SymId::noteheadHeavyXHat, SymId::noteheadHeavyXHat,
267 : SymId::noteheadHeavyXHat },
268 :
269 : { SymId::noteShapeKeystoneWhite, SymId::noteShapeKeystoneWhite, SymId::noteShapeKeystoneBlack,
270 : SymId::noteShapeKeystoneDoubleWhole },
271 : { SymId::noteShapeQuarterMoonWhite, SymId::noteShapeQuarterMoonWhite, SymId::noteShapeQuarterMoonBlack,
272 : SymId::noteShapeQuarterMoonDoubleWhole },
273 : { SymId::noteShapeIsoscelesTriangleWhite, SymId::noteShapeIsoscelesTriangleWhite, SymId::noteShapeIsoscelesTriangleBlack,
274 : SymId::noteShapeIsoscelesTriangleDoubleWhole },
275 : { SymId::noteShapeMoonLeftWhite, SymId::noteShapeMoonLeftWhite, SymId::noteShapeMoonLeftBlack,
276 : SymId::noteShapeMoonLeftDoubleWhole },
277 : { SymId::noteShapeArrowheadLeftWhite, SymId::noteShapeArrowheadLeftWhite, SymId::noteShapeArrowheadLeftBlack,
278 : SymId::noteShapeArrowheadLeftDoubleWhole },
279 : { SymId::noteShapeTriangleRoundLeftWhite, SymId::noteShapeTriangleRoundLeftWhite, SymId::noteShapeTriangleRoundLeftBlack,
280 : SymId::noteShapeTriangleRoundLeftDoubleWhole },
281 :
282 : { SymId::noteDoWhole, SymId::noteDoHalf, SymId::noteDoBlack, SymId::noSym },
283 : { SymId::noteDiWhole, SymId::noteDiHalf, SymId::noteDiBlack, SymId::noSym },
284 : { SymId::noteRaWhole, SymId::noteRaHalf, SymId::noteRaBlack, SymId::noSym },
285 : { SymId::noteReWhole, SymId::noteReHalf, SymId::noteReBlack, SymId::noSym },
286 : { SymId::noteRiWhole, SymId::noteRiHalf, SymId::noteRiBlack, SymId::noSym },
287 : { SymId::noteMeWhole, SymId::noteMeHalf, SymId::noteMeBlack, SymId::noSym },
288 : { SymId::noteMiWhole, SymId::noteMiHalf, SymId::noteMiBlack, SymId::noSym },
289 : { SymId::noteFaWhole, SymId::noteFaHalf, SymId::noteFaBlack, SymId::noSym },
290 : { SymId::noteFiWhole, SymId::noteFiHalf, SymId::noteFiBlack, SymId::noSym },
291 : { SymId::noteSeWhole, SymId::noteSeHalf, SymId::noteSeBlack, SymId::noSym },
292 : { SymId::noteSoWhole, SymId::noteSoHalf, SymId::noteSoBlack, SymId::noSym },
293 : { SymId::noteLeWhole, SymId::noteLeHalf, SymId::noteLeBlack, SymId::noSym },
294 : { SymId::noteLaWhole, SymId::noteLaHalf, SymId::noteLaBlack, SymId::noSym },
295 : { SymId::noteLiWhole, SymId::noteLiHalf, SymId::noteLiBlack, SymId::noSym },
296 : { SymId::noteTeWhole, SymId::noteTeHalf, SymId::noteTeBlack, SymId::noSym },
297 : { SymId::noteTiWhole, SymId::noteTiHalf, SymId::noteTiBlack, SymId::noSym },
298 : { SymId::noteSiWhole, SymId::noteSiHalf, SymId::noteSiBlack, SymId::noSym },
299 :
300 : { SymId::noteASharpWhole, SymId::noteASharpHalf, SymId::noteASharpBlack, SymId::noSym },
301 : { SymId::noteAWhole, SymId::noteAHalf, SymId::noteABlack, SymId::noSym },
302 : { SymId::noteAFlatWhole, SymId::noteAFlatHalf, SymId::noteAFlatBlack, SymId::noSym },
303 : { SymId::noteBSharpWhole, SymId::noteBSharpHalf, SymId::noteBSharpBlack, SymId::noSym },
304 : { SymId::noteBWhole, SymId::noteBHalf, SymId::noteBBlack, SymId::noSym },
305 : { SymId::noteBFlatWhole, SymId::noteBFlatHalf, SymId::noteBFlatBlack, SymId::noSym },
306 : { SymId::noteCSharpWhole, SymId::noteCSharpHalf, SymId::noteCSharpBlack, SymId::noSym },
307 : { SymId::noteCWhole, SymId::noteCHalf, SymId::noteCBlack, SymId::noSym },
308 : { SymId::noteCFlatWhole, SymId::noteCFlatHalf, SymId::noteCFlatBlack, SymId::noSym },
309 : { SymId::noteDSharpWhole, SymId::noteDSharpHalf, SymId::noteDSharpBlack, SymId::noSym },
310 : { SymId::noteDWhole, SymId::noteDHalf, SymId::noteDBlack, SymId::noSym },
311 : { SymId::noteDFlatWhole, SymId::noteDFlatHalf, SymId::noteDFlatBlack, SymId::noSym },
312 : { SymId::noteESharpWhole, SymId::noteESharpHalf, SymId::noteESharpBlack, SymId::noSym },
313 : { SymId::noteEWhole, SymId::noteEHalf, SymId::noteEBlack, SymId::noSym },
314 : { SymId::noteEFlatWhole, SymId::noteEFlatHalf, SymId::noteEFlatBlack, SymId::noSym },
315 : { SymId::noteFSharpWhole, SymId::noteFSharpHalf, SymId::noteFSharpBlack, SymId::noSym },
316 : { SymId::noteFWhole, SymId::noteFHalf, SymId::noteFBlack, SymId::noSym },
317 : { SymId::noteFFlatWhole, SymId::noteFFlatHalf, SymId::noteFFlatBlack, SymId::noSym },
318 : { SymId::noteGSharpWhole, SymId::noteGSharpHalf, SymId::noteGSharpBlack, SymId::noSym },
319 : { SymId::noteGWhole, SymId::noteGHalf, SymId::noteGBlack, SymId::noSym },
320 : { SymId::noteGFlatWhole, SymId::noteGFlatHalf, SymId::noteGFlatBlack, SymId::noSym },
321 : { SymId::noteHWhole, SymId::noteHHalf, SymId::noteHBlack, SymId::noSym },
322 : { SymId::noteHSharpWhole, SymId::noteHSharpHalf, SymId::noteHSharpBlack, SymId::noSym },
323 :
324 : { SymId::noSym, SymId::swissRudimentsNoteheadHalfFlam, SymId::swissRudimentsNoteheadBlackFlam, SymId::noSym },
325 : { SymId::noSym, SymId::swissRudimentsNoteheadHalfDouble, SymId::swissRudimentsNoteheadBlackDouble, SymId::noSym }
326 : }
327 : };
328 :
329 : //---------------------------------------------------------
330 : // noteHead
331 : //---------------------------------------------------------
332 :
333 2 : SymId Note::noteHead(int direction, NoteHeadGroup group, NoteHeadType t)
334 : {
335 2 : return noteHeads[direction][int(group)][int(t)];
336 : }
337 :
338 1436498 : SymId Note::noteHead(int direction, NoteHeadGroup group, NoteHeadType t, int tpc, Key key, NoteHeadScheme scheme)
339 : {
340 : // shortcut
341 1436498 : if (scheme == NoteHeadScheme::HEAD_NORMAL) {
342 1434057 : return noteHeads[direction][int(group)][int(t)];
343 : }
344 : // other schemes
345 2441 : if (scheme == NoteHeadScheme::HEAD_PITCHNAME || scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN
346 656 : || scheme == NoteHeadScheme::HEAD_PITCHNAME_NO_ACCIDENTALS
347 656 : || scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN_NO_ACCIDENTALS) {
348 1785 : const bool no_accidentals = scheme == NoteHeadScheme::HEAD_PITCHNAME_NO_ACCIDENTALS
349 1785 : || scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN_NO_ACCIDENTALS;
350 1785 : const bool german = scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN
351 1785 : || scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN_NO_ACCIDENTALS;
352 :
353 1785 : if (tpc == Tpc::TPC_A) {
354 201 : group = NoteHeadGroup::HEAD_A;
355 1584 : } else if (tpc == Tpc::TPC_B) {
356 137 : if (german) {
357 27 : group = NoteHeadGroup::HEAD_H;
358 : } else {
359 110 : group = NoteHeadGroup::HEAD_B;
360 : }
361 1447 : } else if (tpc == Tpc::TPC_C) {
362 175 : group = NoteHeadGroup::HEAD_C;
363 1272 : } else if (tpc == Tpc::TPC_D) {
364 280 : group = NoteHeadGroup::HEAD_D;
365 992 : } else if (tpc == Tpc::TPC_E) {
366 254 : group = NoteHeadGroup::HEAD_E;
367 738 : } else if (tpc == Tpc::TPC_F) {
368 129 : group = NoteHeadGroup::HEAD_F;
369 609 : } else if (tpc == Tpc::TPC_G) {
370 197 : group = NoteHeadGroup::HEAD_G;
371 412 : } else if (tpc == Tpc::TPC_A_S) {
372 0 : if (no_accidentals) {
373 0 : group = NoteHeadGroup::HEAD_A;
374 : } else {
375 0 : group = NoteHeadGroup::HEAD_A_SHARP;
376 : }
377 412 : } else if (tpc == Tpc::TPC_B_S) {
378 0 : if (no_accidentals) {
379 0 : if (german) {
380 0 : group = NoteHeadGroup::HEAD_H;
381 : } else {
382 0 : group = NoteHeadGroup::HEAD_B;
383 : }
384 : } else {
385 0 : if (german) {
386 0 : group = NoteHeadGroup::HEAD_H_SHARP;
387 : } else {
388 0 : group = NoteHeadGroup::HEAD_B_SHARP;
389 : }
390 : }
391 412 : } else if (tpc == Tpc::TPC_C_S) {
392 84 : if (no_accidentals) {
393 0 : group = NoteHeadGroup::HEAD_C;
394 : } else {
395 84 : group = NoteHeadGroup::HEAD_C_SHARP;
396 : }
397 328 : } else if (tpc == Tpc::TPC_D_S) {
398 28 : if (no_accidentals) {
399 0 : group = NoteHeadGroup::HEAD_D;
400 : } else {
401 28 : group = NoteHeadGroup::HEAD_D_SHARP;
402 : }
403 300 : } else if (tpc == Tpc::TPC_E_S) {
404 0 : if (no_accidentals) {
405 0 : group = NoteHeadGroup::HEAD_E;
406 : } else {
407 0 : group = NoteHeadGroup::HEAD_E_SHARP;
408 : }
409 300 : } else if (tpc == Tpc::TPC_F_S) {
410 112 : if (no_accidentals) {
411 0 : group = NoteHeadGroup::HEAD_F;
412 : } else {
413 112 : group = NoteHeadGroup::HEAD_F_SHARP;
414 : }
415 188 : } else if (tpc == Tpc::TPC_G_S) {
416 44 : if (no_accidentals) {
417 0 : group = NoteHeadGroup::HEAD_G;
418 : } else {
419 44 : group = NoteHeadGroup::HEAD_G_SHARP;
420 : }
421 144 : } else if (tpc == Tpc::TPC_A_B) {
422 44 : if (no_accidentals) {
423 0 : group = NoteHeadGroup::HEAD_A;
424 : } else {
425 44 : group = NoteHeadGroup::HEAD_A_FLAT;
426 : }
427 100 : } else if (tpc == Tpc::TPC_B_B) {
428 28 : if (no_accidentals) {
429 0 : group = NoteHeadGroup::HEAD_B;
430 : } else {
431 28 : if (german) {
432 28 : group = NoteHeadGroup::HEAD_B;
433 : } else {
434 0 : group = NoteHeadGroup::HEAD_B_FLAT;
435 : }
436 : }
437 72 : } else if (tpc == Tpc::TPC_C_B) {
438 0 : if (no_accidentals) {
439 0 : group = NoteHeadGroup::HEAD_C;
440 : } else {
441 0 : group = NoteHeadGroup::HEAD_C_FLAT;
442 : }
443 72 : } else if (tpc == Tpc::TPC_D_B) {
444 0 : if (no_accidentals) {
445 0 : group = NoteHeadGroup::HEAD_D;
446 : } else {
447 0 : group = NoteHeadGroup::HEAD_D_FLAT;
448 : }
449 72 : } else if (tpc == Tpc::TPC_E_B) {
450 72 : if (no_accidentals) {
451 0 : group = NoteHeadGroup::HEAD_E;
452 : } else {
453 72 : group = NoteHeadGroup::HEAD_E_FLAT;
454 : }
455 0 : } else if (tpc == Tpc::TPC_F_B) {
456 0 : if (no_accidentals) {
457 0 : group = NoteHeadGroup::HEAD_F;
458 : } else {
459 0 : group = NoteHeadGroup::HEAD_F_FLAT;
460 : }
461 0 : } else if (tpc == Tpc::TPC_G_B) {
462 0 : if (no_accidentals) {
463 0 : group = NoteHeadGroup::HEAD_G;
464 : } else {
465 0 : group = NoteHeadGroup::HEAD_G_FLAT;
466 : }
467 0 : } else if (no_accidentals) {
468 : // for TPCs that don't have their own heads (i.e. with 2 or 3 accidentals),
469 : // fall back to using the corresponding ones without accidentals
470 : // rather than using the (smaller!) normal noteheads
471 0 : if (tpc == Tpc::TPC_A_SS || tpc == Tpc::TPC_A_SSS) {
472 0 : group = NoteHeadGroup::HEAD_A;
473 0 : } else if (tpc == Tpc::TPC_B_SS || tpc == Tpc::TPC_B_SSS) {
474 0 : if (german) {
475 0 : group = NoteHeadGroup::HEAD_H;
476 : } else {
477 0 : group = NoteHeadGroup::HEAD_B;
478 : }
479 0 : } else if (tpc == Tpc::TPC_C_SS || tpc == Tpc::TPC_C_SSS) {
480 0 : group = NoteHeadGroup::HEAD_C;
481 0 : } else if (tpc == Tpc::TPC_D_SS || tpc == Tpc::TPC_D_SSS) {
482 0 : group = NoteHeadGroup::HEAD_D;
483 0 : } else if (tpc == Tpc::TPC_E_SS || tpc == Tpc::TPC_E_SSS) {
484 0 : group = NoteHeadGroup::HEAD_E;
485 0 : } else if (tpc == Tpc::TPC_F_SS || tpc == Tpc::TPC_F_SSS) {
486 0 : group = NoteHeadGroup::HEAD_F;
487 0 : } else if (tpc == Tpc::TPC_G_SS || tpc == Tpc::TPC_G_SSS) {
488 0 : group = NoteHeadGroup::HEAD_G;
489 0 : } else if (tpc == Tpc::TPC_A_BB || tpc == Tpc::TPC_A_BBB) {
490 0 : group = NoteHeadGroup::HEAD_A;
491 0 : } else if (tpc == Tpc::TPC_B_BB || tpc == Tpc::TPC_B_BBB) {
492 0 : if (german) {
493 0 : group = NoteHeadGroup::HEAD_H;
494 : } else {
495 0 : group = NoteHeadGroup::HEAD_B;
496 : }
497 0 : } else if (tpc == Tpc::TPC_C_BB || tpc == Tpc::TPC_C_BBB) {
498 0 : group = NoteHeadGroup::HEAD_C;
499 0 : } else if (tpc == Tpc::TPC_D_BB || tpc == Tpc::TPC_D_BBB) {
500 0 : group = NoteHeadGroup::HEAD_D;
501 0 : } else if (tpc == Tpc::TPC_E_BB || tpc == Tpc::TPC_E_BBB) {
502 0 : group = NoteHeadGroup::HEAD_E;
503 0 : } else if (tpc == Tpc::TPC_F_BB || tpc == Tpc::TPC_F_BBB) {
504 0 : group = NoteHeadGroup::HEAD_F;
505 0 : } else if (tpc == Tpc::TPC_G_BB || tpc == Tpc::TPC_G_BBB) {
506 0 : group = NoteHeadGroup::HEAD_G;
507 : }
508 : }
509 2441 : } else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_4) {
510 0 : int degree = tpc2degree(tpc, key);
511 0 : switch (degree) {
512 0 : case 0:
513 : case 3:
514 0 : group = NoteHeadGroup::HEAD_FA;
515 0 : break;
516 0 : case 1:
517 : case 4:
518 0 : group = NoteHeadGroup::HEAD_SOL;
519 0 : break;
520 0 : case 2:
521 : case 5:
522 0 : group = NoteHeadGroup::HEAD_LA;
523 0 : break;
524 0 : case 6:
525 0 : group = NoteHeadGroup::HEAD_MI;
526 0 : break;
527 : }
528 656 : } else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_AIKIN
529 656 : || scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_FUNK
530 656 : || scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_WALKER) {
531 0 : int degree = tpc2degree(tpc, key);
532 0 : switch (degree) {
533 0 : case 0:
534 0 : if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_AIKIN) {
535 0 : group = NoteHeadGroup::HEAD_DO;
536 0 : } else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_FUNK) {
537 0 : group = NoteHeadGroup::HEAD_DO_FUNK;
538 0 : } else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_WALKER) {
539 0 : group = NoteHeadGroup::HEAD_DO_WALKER;
540 : }
541 0 : break;
542 0 : case 1:
543 0 : if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_AIKIN) {
544 0 : group = NoteHeadGroup::HEAD_RE;
545 0 : } else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_FUNK) {
546 0 : group = NoteHeadGroup::HEAD_RE_FUNK;
547 0 : } else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_WALKER) {
548 0 : group = NoteHeadGroup::HEAD_RE_WALKER;
549 : }
550 0 : break;
551 0 : case 2:
552 0 : group = NoteHeadGroup::HEAD_MI;
553 0 : break;
554 0 : case 3:
555 0 : group = NoteHeadGroup::HEAD_FA;
556 0 : break;
557 0 : case 4:
558 0 : group = NoteHeadGroup::HEAD_SOL;
559 0 : break;
560 0 : case 5:
561 0 : group = NoteHeadGroup::HEAD_LA;
562 0 : break;
563 0 : case 6:
564 0 : if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_AIKIN) {
565 0 : group = NoteHeadGroup::HEAD_TI;
566 0 : } else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_FUNK) {
567 0 : group = NoteHeadGroup::HEAD_TI_FUNK;
568 0 : } else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_WALKER) {
569 0 : group = NoteHeadGroup::HEAD_TI_WALKER;
570 : }
571 0 : break;
572 : }
573 656 : } else if (scheme == NoteHeadScheme::HEAD_SOLFEGE) {
574 383 : int degree = tpc2degree(tpc, key);
575 383 : int alteration = tpc2alterByKey(tpc, key);
576 383 : if (degree == 0 && alteration == 0) {
577 0 : group = NoteHeadGroup::HEAD_DO_NAME;
578 383 : } else if (degree == 0 && alteration == 1) {
579 48 : group = NoteHeadGroup::HEAD_DI_NAME;
580 335 : } else if (degree == 1 && alteration == -1) {
581 44 : group = NoteHeadGroup::HEAD_RA_NAME;
582 291 : } else if (degree == 1 && alteration == 0) {
583 0 : group = NoteHeadGroup::HEAD_RE_NAME;
584 291 : } else if (degree == 1 && alteration == 1) {
585 44 : group = NoteHeadGroup::HEAD_RI_NAME;
586 247 : } else if (degree == 2 && alteration == -1) {
587 44 : group = NoteHeadGroup::HEAD_ME_NAME;
588 203 : } else if (degree == 2 && alteration == 0) {
589 0 : group = NoteHeadGroup::HEAD_MI_NAME;
590 203 : } else if (degree == 3 && alteration == 0) {
591 0 : group = NoteHeadGroup::HEAD_FA_NAME;
592 203 : } else if (degree == 3 && alteration == 1) {
593 44 : group = NoteHeadGroup::HEAD_FI_NAME;
594 159 : } else if (degree == 4 && alteration == -1) {
595 44 : group = NoteHeadGroup::HEAD_SE_NAME;
596 115 : } else if (degree == 4 && alteration == 0) {
597 43 : group = NoteHeadGroup::HEAD_SOL_NAME;
598 72 : } else if (degree == 4 && alteration == 1) {
599 0 : group = NoteHeadGroup::HEAD_SI_NAME;
600 72 : } else if (degree == 5 && alteration == -1) {
601 0 : group = NoteHeadGroup::HEAD_LE_NAME;
602 72 : } else if (degree == 5 && alteration == 0) {
603 0 : group = NoteHeadGroup::HEAD_LA_NAME;
604 72 : } else if (degree == 5 && alteration == 1) {
605 44 : group = NoteHeadGroup::HEAD_LI_NAME;
606 28 : } else if (degree == 6 && alteration == -1) {
607 28 : group = NoteHeadGroup::HEAD_TE_NAME;
608 0 : } else if (degree == 6 && alteration == 0) {
609 0 : group = NoteHeadGroup::HEAD_TI_NAME;
610 : }
611 273 : } else if (scheme == NoteHeadScheme::HEAD_SOLFEGE_FIXED) {
612 273 : Char stepName = tpc2stepName(tpc);
613 273 : if (stepName == u'C') {
614 74 : group = NoteHeadGroup::HEAD_DO_NAME;
615 199 : } else if (stepName == u'D') {
616 43 : group = NoteHeadGroup::HEAD_RE_NAME;
617 156 : } else if (stepName == u'E') {
618 43 : group = NoteHeadGroup::HEAD_MI_NAME;
619 113 : } else if (stepName == u'F') {
620 43 : group = NoteHeadGroup::HEAD_FA_NAME;
621 70 : } else if (stepName == u'G') {
622 0 : group = NoteHeadGroup::HEAD_SOL_NAME;
623 70 : } else if (stepName == u'A') {
624 43 : group = NoteHeadGroup::HEAD_LA_NAME;
625 27 : } else if (stepName == u'B') {
626 27 : group = NoteHeadGroup::HEAD_SI_NAME;
627 : }
628 : }
629 2441 : return noteHeads[direction][int(group)][int(t)];
630 : }
631 :
632 0 : NoteHead::NoteHead(DummyParentOr<Note> parent)
633 0 : : Symbol(ElementType::NOTEHEAD, parent) {}
634 :
635 : //---------------------------------------------------------
636 : // headGroup
637 : // used only when dropping a notehead from the palette
638 : // they are either half note, either double whole
639 : //---------------------------------------------------------
640 :
641 0 : NoteHeadGroup NoteHead::headGroup() const
642 : {
643 0 : NoteHeadGroup group = NoteHeadGroup::HEAD_INVALID;
644 0 : for (int i = 0; i < int(NoteHeadGroup::HEAD_DO_WALKER); ++i) {
645 0 : if (noteHeads[0][i][1] == m_sym || noteHeads[0][i][3] == m_sym) {
646 0 : group = (NoteHeadGroup)i;
647 0 : break;
648 : }
649 : }
650 0 : return group;
651 : }
652 :
653 : //---------------------------------------------------------
654 : // Note
655 : //---------------------------------------------------------
656 :
657 35940 : Note::Note(DummyParentOr<Chord> ch)
658 35940 : : EngravingItem(ElementType::NOTE, ch, ElementFlag::MOVABLE)
659 : {
660 35940 : m_playEvents.push_back(NoteEvent()); // add default play event
661 35940 : }
662 :
663 73806 : Note::~Note()
664 : {
665 36903 : delete m_accidental;
666 36903 : muse::DeleteAll(m_el);
667 :
668 36903 : if (m_tieFor && m_tieFor->parent() == this) {
669 783 : delete m_tieFor;
670 : }
671 :
672 36903 : muse::DeleteAll(m_dots);
673 73806 : }
674 :
675 7436 : std::vector<Note*> Note::compoundNotes() const
676 : {
677 7436 : std::vector<Note*> elements;
678 7436 : if (Note* note = firstTiedNote()) {
679 7436 : elements.push_back(note);
680 : }
681 :
682 7436 : if (Note* note = lastTiedNote()) {
683 7436 : elements.push_back(note);
684 : }
685 :
686 7437 : for (Spanner* e : m_spannerFor) {
687 1 : elements.push_back(toNote(e->endElement()));
688 : }
689 7437 : for (Spanner* e : m_spannerBack) {
690 1 : elements.push_back(toNote(e->startElement()));
691 : }
692 :
693 7436 : Beam* beam = chord()->beam();
694 7436 : if (beam) {
695 2796 : for (EngravingItem* e : beam->elements()) {
696 2167 : if (e && e->isNote() && e != this) {
697 0 : elements.push_back(toNote(e));
698 : }
699 : }
700 : }
701 :
702 7436 : return elements;
703 0 : }
704 :
705 5838 : Note::Note(const Note& n, bool link)
706 5838 : : EngravingItem(n, link)
707 : {
708 5838 : if (link) {
709 1078 : score()->undo(new Link(this, const_cast<Note*>(&n)));
710 : }
711 5838 : m_subchannel = n.m_subchannel;
712 5838 : m_line = n.m_line;
713 5838 : m_fret = n.m_fret;
714 5838 : m_harmonicFret = n.m_harmonicFret;
715 5838 : m_displayFret = n.m_displayFret;
716 5838 : m_string = n.m_string;
717 5838 : m_fretConflict = n.m_fretConflict;
718 5838 : m_ghost = n.m_ghost;
719 5838 : m_deadNote = n.m_deadNote;
720 5838 : m_pitch = n.m_pitch;
721 5838 : m_centOffset = n.m_centOffset;
722 5838 : m_tpc[0] = n.m_tpc[0];
723 5838 : m_tpc[1] = n.m_tpc[1];
724 5838 : m_dotsHidden = n.m_dotsHidden;
725 5838 : m_hidden = n.m_hidden;
726 5838 : m_play = n.m_play;
727 5838 : m_tuning = n.m_tuning;
728 5838 : m_veloType = n.m_veloType;
729 5838 : m_userVelocity = n.m_userVelocity;
730 5838 : m_headScheme = n.m_headScheme;
731 5838 : m_headGroup = n.m_headGroup;
732 5838 : m_headType = n.m_headType;
733 5838 : m_userMirror = n.m_userMirror;
734 5838 : m_isSmall = n.m_isSmall;
735 5838 : m_userDotPosition = n.m_userDotPosition;
736 5838 : m_fixed = n.m_fixed;
737 5838 : m_fixedLine = n.m_fixedLine;
738 5838 : m_harmonic = n.m_harmonic;
739 5838 : m_hasParens = n.m_hasParens;
740 5838 : m_hideGeneratedParens = n.m_hideGeneratedParens;
741 :
742 5838 : if (n.m_accidental) {
743 30 : add(new Accidental(*(n.m_accidental)));
744 : }
745 :
746 : // types in _el: SYMBOL, IMAGE, FINGERING, TEXT, BEND
747 5838 : const Staff* stf = staff();
748 5838 : bool tabFingering = stf ? stf->staffTypeForElement(this)->showTabFingering() : false;
749 5863 : for (EngravingItem* e : n.m_el) {
750 25 : if (e->isFingering() && staff()->isTabStaff(tick()) && !tabFingering) { // tablature has no fingering
751 0 : continue;
752 : }
753 25 : EngravingItem* ce = e->clone();
754 25 : add(ce);
755 25 : if (link) {
756 25 : score()->undo(new Link(ce, const_cast<EngravingItem*>(e)));
757 : }
758 : }
759 :
760 5838 : m_playEvents = n.m_playEvents;
761 :
762 5838 : if (n.laissezVib()) {
763 0 : m_tieFor = Factory::copyLaissezVib(*toLaissezVib(n.m_tieFor));
764 0 : m_tieFor->setOwnershipParent(this);
765 0 : m_tieFor->setStartNote(this);
766 0 : m_tieFor->setTick(tick());
767 0 : if (link) {
768 0 : score()->undo(new Link(m_tieFor, n.m_tieFor));
769 : }
770 5838 : } else if (n.outgoingPartialTie()) {
771 0 : setTieFor(Factory::copyPartialTie(*toPartialTie(n.m_tieFor)));
772 0 : m_tieFor->setOwnershipParent(this);
773 0 : m_tieFor->setStartNote(this);
774 0 : m_tieFor->setTick(tick());
775 0 : if (link) {
776 0 : score()->undo(new Link(m_tieFor, n.m_tieFor));
777 : }
778 5838 : } else if (n.m_tieFor) {
779 425 : m_tieFor = Factory::copyTie(*n.m_tieFor);
780 425 : m_tieFor->setStartNote(this);
781 425 : m_tieFor->setTick(tick());
782 425 : m_tieFor->setEndNote(0);
783 : }
784 :
785 5838 : if (n.incomingPartialTie()) {
786 0 : setTieBack(Factory::copyPartialTie(*toPartialTie(n.m_tieBack)));
787 0 : m_tieBack->setOwnershipParent(this);
788 0 : m_tieBack->setEndNote(this);
789 0 : m_tieBack->setTick(tick());
790 0 : if (link) {
791 0 : score()->undo(new Link(m_tieBack, n.m_tieBack));
792 : }
793 : }
794 :
795 5872 : for (NoteDot* dot : n.m_dots) {
796 34 : add(Factory::copyNoteDot(*dot));
797 : }
798 5838 : m_mark = n.m_mark;
799 :
800 5838 : setDropTarget(false);
801 5838 : }
802 :
803 338 : void Note::setOwnershipParent(DummyParentOr<Chord> ch)
804 : {
805 338 : EngravingItem::setOwnershipParent(ch);
806 338 : }
807 :
808 : //---------------------------------------------------------
809 : // concertPitchIdx
810 : //---------------------------------------------------------
811 :
812 1773487 : inline int Note::concertPitchIdx() const
813 : {
814 1773487 : return concertPitch() ? 0 : 1;
815 : }
816 :
817 : //---------------------------------------------------------
818 : // setPitch
819 : //---------------------------------------------------------
820 :
821 39470 : void Note::setPitch(int val, bool notifyAboutChanged)
822 : {
823 39470 : assert(pitchIsValid(val));
824 39470 : if (m_pitch != val) {
825 37429 : m_pitch = val;
826 :
827 37429 : if (notifyAboutChanged) {
828 : #ifndef ENGRAVING_NO_ACCESSIBILITY
829 19810 : notifyAboutNameChanged();
830 : #endif
831 : }
832 : }
833 39470 : }
834 :
835 7787 : void Note::setPitch(int pitch, int tpc1, int tpc2)
836 : {
837 7787 : assert(tpcIsValid(tpc1));
838 7787 : assert(tpcIsValid(tpc2));
839 7787 : m_tpc[0] = tpc1;
840 7787 : m_tpc[1] = tpc2;
841 7787 : setPitch(pitch);
842 7787 : }
843 :
844 : //---------------------------------------------------------
845 : // tpc1default
846 : //---------------------------------------------------------
847 :
848 0 : int Note::tpc1default(int p) const
849 : {
850 0 : Key key = Key::C;
851 0 : if (staff() && chord()) {
852 0 : Fraction tick = chord()->tick();
853 0 : key = staff()->concertKey(tick);
854 : }
855 0 : return pitch2tpc(p, key, Prefer::NEAREST);
856 : }
857 :
858 : //---------------------------------------------------------
859 : // tpc2default
860 : //---------------------------------------------------------
861 :
862 0 : int Note::tpc2default(int p) const
863 : {
864 0 : Key key = Key::C;
865 0 : if (staff() && chord()) {
866 0 : Fraction tick = chord()->tick();
867 0 : key = staff()->key(tick);
868 0 : if (concertPitch()) {
869 0 : Interval interval = originPart()->instrument(tick)->transpose();
870 0 : if (!interval.isZero()) {
871 0 : interval.flip();
872 0 : key = Transpose::transposeKey(key, interval);
873 : }
874 : }
875 : }
876 0 : return pitch2tpc(p - transposition(), key, Prefer::NEAREST);
877 : }
878 :
879 : //---------------------------------------------------------
880 : // setTpcFromPitch
881 : //---------------------------------------------------------
882 :
883 5229 : void Note::setTpcFromPitch(Prefer prefer /* = Prefer::NEAREST */)
884 : {
885 : // works best if note is already added to score, otherwise we can't determine transposition or key
886 5229 : Fraction tick = chord() ? chord()->tick() : Fraction(-1, 1);
887 5229 : Interval v = staff() ? staff()->transpose(tick) : Interval();
888 5229 : Key cKey = (staff() && chord()) ? staff()->concertKey(chord()->tick()) : Key::C;
889 : // set concert pitch tpc
890 5229 : m_tpc[0] = pitch2tpc(m_pitch, cKey, prefer);
891 : // set transposed tpc
892 5229 : if (v.isZero()) {
893 3085 : m_tpc[1] = m_tpc[0];
894 : } else {
895 2144 : v.flip();
896 2144 : m_tpc[1] = Transpose::transposeTpc(m_tpc[0], v, true);
897 : }
898 5229 : assert(tpcIsValid(m_tpc[0]));
899 5229 : assert(tpcIsValid(m_tpc[1]));
900 5229 : }
901 :
902 : //---------------------------------------------------------
903 : // setTpc
904 : //---------------------------------------------------------
905 :
906 6 : void Note::setTpc(int v)
907 : {
908 6 : IF_ASSERT_FAILED(tpcIsValid(v)) {
909 0 : return;
910 : }
911 6 : m_tpc[concertPitchIdx()] = v;
912 : }
913 :
914 : //---------------------------------------------------------
915 : // undoSetTpc
916 : // change the current tpc
917 : //---------------------------------------------------------
918 :
919 130 : void Note::undoSetTpc(int v)
920 : {
921 130 : if (concertPitch()) {
922 0 : if (v != tpc1()) {
923 0 : undoChangeProperty(Pid::TPC1, v);
924 : }
925 : } else {
926 130 : if (v != tpc2()) {
927 130 : undoChangeProperty(Pid::TPC2, v);
928 : }
929 : }
930 130 : }
931 :
932 : //---------------------------------------------------------
933 : // tpc
934 : //---------------------------------------------------------
935 :
936 1773481 : int Note::tpc() const
937 : {
938 1773481 : return m_tpc[concertPitchIdx()];
939 : }
940 :
941 : //---------------------------------------------------------
942 : // tpcUserName
943 : //---------------------------------------------------------
944 :
945 0 : String Note::tpcUserName(const bool explicitAccidental, bool full) const
946 : {
947 0 : String pitchName = engraving::tpcUserName(tpc(), epitch() + ottaveCapoFret(), explicitAccidental, full);
948 :
949 0 : pitchName = muse::mtrc("global/pitchName", pitchName);
950 :
951 0 : if (fixed() && headGroup() == NoteHeadGroup::HEAD_SLASH) {
952 : // see Note::accessibleInfo(), but we return what we have
953 0 : return pitchName;
954 : }
955 0 : if (staff()->isDrumStaff(tick()) && originPart()->instrument(tick())->drumset()) {
956 : // see Note::accessibleInfo(), but we return what we have
957 0 : return pitchName;
958 : }
959 0 : if (staff()->isTabStaff(tick())) {
960 : // no further translation
961 0 : return pitchName;
962 : }
963 :
964 0 : String pitchOffset;
965 0 : if (tuning() != 0) {
966 : static constexpr size_t bufferSize = 50;
967 : char buffer[bufferSize];
968 0 : snprintf(buffer, bufferSize, "%+.3f", tuning());
969 0 : pitchOffset = String::fromAscii(buffer);
970 : }
971 :
972 0 : if (!concertPitch() && transposition()) {
973 0 : String soundingPitch = engraving::tpcUserName(tpc1(), ppitch(), explicitAccidental);
974 0 : soundingPitch = muse::mtrc("global/pitchName", soundingPitch);
975 0 : return muse::mtrc("engraving", "%1 (sounding as %2%3)").arg(pitchName, soundingPitch, pitchOffset);
976 0 : }
977 0 : return pitchName + pitchOffset;
978 0 : }
979 :
980 : //---------------------------------------------------------
981 : // transposeTpc
982 : // return transposed tpc
983 : // If in concertPitch mode return tpc for transposed view
984 : // else return tpc for concert pitch view.
985 : //---------------------------------------------------------
986 :
987 308 : int Note::transposeTpc(int tpc) const
988 : {
989 308 : Fraction tick = chord() ? chord()->tick() : Fraction(-1, 1);
990 308 : Interval v = staff()->transpose(tick);
991 308 : if (v.isZero()) {
992 35 : return tpc;
993 : }
994 273 : if (concertPitch()) {
995 0 : v.flip();
996 0 : return Transpose::transposeTpc(tpc, v, true);
997 : } else {
998 273 : return Transpose::transposeTpc(tpc, v, true);
999 : }
1000 : }
1001 :
1002 1574 : int Note::playingTpc() const
1003 : {
1004 1574 : int result = tpc();
1005 :
1006 1574 : if (!concertPitch() && transposition()) {
1007 88 : int tpc1 = this->tpc1();
1008 88 : if (tpc1 == Tpc::TPC_INVALID) {
1009 0 : result = transposeTpc(result);
1010 : } else {
1011 88 : result = tpc1;
1012 : }
1013 : }
1014 :
1015 1574 : int steps = ottaveCapoFret();
1016 1574 : if (steps != 0) {
1017 0 : result = Transpose::transposeTpc(result, Interval(steps), true);
1018 : }
1019 :
1020 1574 : return result;
1021 : }
1022 :
1023 : //---------------------------------------------------------
1024 : // noteHead
1025 : //---------------------------------------------------------
1026 :
1027 1436498 : SymId Note::noteHead() const
1028 : {
1029 : int up;
1030 : NoteHeadType ht;
1031 1436498 : if (chord()) {
1032 1436498 : up = chord()->up();
1033 1436498 : ht = chord()->durationType().headType();
1034 : } else {
1035 0 : up = 1;
1036 0 : ht = NoteHeadType::HEAD_QUARTER;
1037 : }
1038 1436498 : if (m_headType != NoteHeadType::HEAD_AUTO) {
1039 1500 : ht = m_headType;
1040 : }
1041 :
1042 1436498 : const Staff* st = chord() ? chord()->staff() : nullptr;
1043 :
1044 1436498 : NoteHeadGroup headGroup = m_headGroup;
1045 1436498 : if (m_headGroup == NoteHeadGroup::HEAD_CUSTOM) {
1046 0 : if (st) {
1047 0 : if (st->staffTypeForElement(chord())->isDrumStaff()) {
1048 0 : Fraction t = chord()->tick();
1049 0 : Instrument* inst = originPart()->instrument(t);
1050 0 : Drumset* d = inst->drumset();
1051 0 : if (d) {
1052 0 : return d->noteHeads(m_pitch, ht);
1053 : } else {
1054 0 : LOGD("no drumset");
1055 0 : return noteHead(up, NoteHeadGroup::HEAD_NORMAL, ht);
1056 : }
1057 : } else {
1058 0 : headGroup = NoteHeadGroup::HEAD_NORMAL;
1059 : }
1060 : } else {
1061 0 : return ldata()->cachedNoteheadSym.value();
1062 : }
1063 : }
1064 :
1065 1436498 : Key key = Key::C;
1066 1436498 : NoteHeadScheme scheme = m_headScheme;
1067 1436498 : if (st) {
1068 1436498 : Fraction tick = chord()->tick();
1069 1436498 : if (tick >= Fraction(0, 1)) {
1070 1436498 : key = st->key(tick);
1071 1436498 : if (scheme == NoteHeadScheme::HEAD_AUTO) {
1072 1434057 : scheme = st->staffTypeForElement(chord())->noteHeadScheme();
1073 : }
1074 : }
1075 : }
1076 1436498 : if (scheme == NoteHeadScheme::HEAD_AUTO) {
1077 0 : scheme = NoteHeadScheme::HEAD_NORMAL;
1078 : }
1079 1436498 : SymId t = noteHead(up, headGroup, ht, tpc(), key, scheme);
1080 1436498 : if (t == SymId::noSym) {
1081 0 : LOGD("invalid notehead %d/%d", int(headGroup), int(ht));
1082 0 : t = noteHead(up, NoteHeadGroup::HEAD_NORMAL, ht);
1083 : }
1084 1436498 : return t;
1085 : }
1086 :
1087 : //---------------------------------------------------------
1088 : // headWidth
1089 : //
1090 : // returns the x of the symbol bbox. It is different from headWidth() because zero point could be different from leftmost bbox position.
1091 : //---------------------------------------------------------
1092 59861 : double Note::bboxRightPos() const
1093 : {
1094 59861 : const auto& bbox = score()->engravingFont()->bbox(noteHead(), magS());
1095 119722 : return bbox.right();
1096 : }
1097 :
1098 : //---------------------------------------------------------
1099 : // headBodyWidth
1100 : //
1101 : // returns the width of the notehead "body". It is actual for slashed noteheads like -O-, where O is body.
1102 : //---------------------------------------------------------
1103 254205 : double Note::headBodyWidth() const
1104 : {
1105 254205 : const StaffType* st = staffType();
1106 254205 : if (st && st->isTabStaff()) {
1107 333 : return tabHeadWidth(st);
1108 : }
1109 253872 : return headWidth() + 2 * bboxXShift();
1110 : }
1111 :
1112 : //---------------------------------------------------------
1113 : // outsideTieAttachX
1114 : //
1115 : // returns the X-position for tie attachment for this particular notehead
1116 : //---------------------------------------------------------
1117 0 : double Note::outsideTieAttachX(bool up) const
1118 : {
1119 0 : double xo = 0;
1120 :
1121 : // tab staff notes just use center of bounding box
1122 0 : if (staffType()->isTabStaff()) {
1123 0 : return x() + ((width() / 2) * mag());
1124 : }
1125 : // special cases:
1126 0 : if (m_headGroup == NoteHeadGroup::HEAD_SLASH) {
1127 0 : xo = (up ? headWidth() * 0.75 : headWidth() * 0.25);
1128 0 : if (chord()->durationType().hasStem()) {
1129 : // for quarters and halves, we can safely move a little bit outwards
1130 0 : xo += spatium() * 0.13 * (chord()->up() ? -mag() : mag());
1131 : }
1132 0 : return x() + xo;
1133 : }
1134 0 : if (m_headGroup == NoteHeadGroup::HEAD_SLASHED1 || m_headGroup == NoteHeadGroup::HEAD_SLASHED2) {
1135 : // just use the very center of the notehead
1136 0 : return x() + ((headBodyWidth() / 2) * mag());
1137 : }
1138 : /* Noteheads do not have optical centers at this time, but here's the code
1139 : to future-proof
1140 : xo = symSmuflAnchor(noteHead(), SmuflAnchorId::opticalCenter).x() * mag();
1141 : if (xo > 0) {
1142 : return x() + xo;
1143 : }
1144 : */
1145 : // try for average of cutouts
1146 0 : if (up) {
1147 0 : double xNE = symSmuflAnchor(noteHead(), SmuflAnchorId::cutOutNE).x();
1148 0 : double xNW = symSmuflAnchor(noteHead(), SmuflAnchorId::cutOutNW).x();
1149 0 : xo = ((xNE + xNW) / 2);
1150 0 : if (xNE < xNW) {
1151 : // musejazz is busted
1152 0 : xo = 0;
1153 : }
1154 : } else {
1155 0 : double xSE = symSmuflAnchor(noteHead(), SmuflAnchorId::cutOutSE).x();
1156 0 : double xSW = symSmuflAnchor(noteHead(), SmuflAnchorId::cutOutSW).x();
1157 0 : xo = ((xSE + xSW) / 2);
1158 0 : if (xSE < xSW) {
1159 0 : xo = 0;
1160 : }
1161 : }
1162 0 : if (xo > 0) {
1163 0 : return x() + xo;
1164 : }
1165 : // no cutout, not a slash head, default to middle of notehead
1166 0 : return x() + (headWidth() / 2);
1167 : }
1168 :
1169 0 : void Note::updateHeadGroup(const NoteHeadGroup headGroup)
1170 : {
1171 0 : NoteHeadGroup group = headGroup;
1172 :
1173 0 : if (group == NoteHeadGroup::HEAD_INVALID) {
1174 0 : LOGD("unknown notehead");
1175 0 : group = NoteHeadGroup::HEAD_NORMAL;
1176 : }
1177 :
1178 0 : if (group == m_headGroup) {
1179 0 : return;
1180 : }
1181 :
1182 0 : if (links()) {
1183 0 : for (EngravingObject* scoreElement : *links()) {
1184 0 : scoreElement->undoChangeProperty(Pid::HEAD_GROUP, group);
1185 0 : Note* note = toNote(scoreElement);
1186 :
1187 0 : if (note->staff() && !note->staff()->isDrumStaff(chord()->tick()) && group == NoteHeadGroup::HEAD_CROSS) {
1188 0 : scoreElement->undoChangeProperty(Pid::DEAD, true);
1189 : }
1190 : }
1191 : } else {
1192 0 : undoChangeProperty(Pid::HEAD_GROUP, group);
1193 : }
1194 : }
1195 :
1196 : //---------------------------------------------------------
1197 : // headWidth
1198 : //
1199 : // returns the width of the symbol bbox
1200 : // or the width of the string representation of the fret mark
1201 : //---------------------------------------------------------
1202 :
1203 327083 : double Note::headWidth() const
1204 : {
1205 327083 : return symWidth(noteHead());
1206 : }
1207 :
1208 : //---------------------------------------------------------
1209 : // bboxXShift
1210 : //
1211 : // returns the x shift of the notehead bounding box
1212 : //---------------------------------------------------------
1213 274867 : double Note::bboxXShift() const
1214 : {
1215 274867 : const auto& bbox = score()->engravingFont()->bbox(noteHead(), magS());
1216 274867 : return bbox.bottomLeft().x();
1217 : }
1218 :
1219 : //---------------------------------------------------------
1220 : // noteheadCenterX
1221 : //
1222 : // returns the x coordinate of the notehead center related to the basepoint of the notehead bbox
1223 : //---------------------------------------------------------
1224 9099 : double Note::noteheadCenterX() const
1225 : {
1226 9099 : return score()->engravingFont()->width(noteHead(), magS()) / 2 + bboxXShift();
1227 : }
1228 :
1229 : //---------------------------------------------------------
1230 : // tabHeadWidth
1231 : //---------------------------------------------------------
1232 :
1233 3859 : double Note::tabHeadWidth(const StaffType* tab) const
1234 : {
1235 : double val;
1236 3859 : if (tab && tab->isTabStaff() && m_fret != INVALID_FRET_INDEX && m_string != INVALID_STRING_INDEX) {
1237 2036 : Font f = tab->fretFont();
1238 2036 : f.setPointSizeF(tab->fretFontSize());
1239 2036 : val = FontMetrics::width(f, m_fretString) * magS();
1240 2036 : } else {
1241 1823 : val = headWidth();
1242 : }
1243 3859 : return val;
1244 : }
1245 :
1246 : //---------------------------------------------------------
1247 : // headHeight
1248 : //
1249 : // returns the height of the notehead symbol
1250 : // or the height of the string representation of the fret mark
1251 : //---------------------------------------------------------
1252 :
1253 8170 : double Note::headHeight() const
1254 : {
1255 8170 : return symHeight(noteHead());
1256 : }
1257 :
1258 : //---------------------------------------------------------
1259 : // tabHeadHeight
1260 : //---------------------------------------------------------
1261 :
1262 0 : double Note::tabHeadHeight(const StaffType* tab) const
1263 : {
1264 0 : if (tab && m_fret != INVALID_FRET_INDEX && m_string != INVALID_STRING_INDEX) {
1265 0 : return tab->fretBoxH() * magS();
1266 : }
1267 0 : return headHeight();
1268 : }
1269 :
1270 : //---------------------------------------------------------
1271 : // stemDownNW
1272 : //---------------------------------------------------------
1273 :
1274 331475 : PointF Note::stemDownNW() const
1275 : {
1276 331475 : return symSmuflAnchor(noteHead(), SmuflAnchorId::stemDownNW);
1277 : }
1278 :
1279 : //---------------------------------------------------------
1280 : // stemUpSE
1281 : //---------------------------------------------------------
1282 :
1283 362662 : PointF Note::stemUpSE() const
1284 : {
1285 362662 : return symSmuflAnchor(noteHead(), SmuflAnchorId::stemUpSE);
1286 : }
1287 :
1288 : //---------------------------------------------------------
1289 : // playTicks
1290 : /// Return total tick len of tied notes
1291 : //---------------------------------------------------------
1292 :
1293 722 : int Note::playTicks() const
1294 : {
1295 722 : return playTicksFraction().ticks();
1296 : }
1297 :
1298 : //---------------------------------------------------------
1299 : // playTicksFraction
1300 : /// Return total tick len of tied notes
1301 : //---------------------------------------------------------
1302 :
1303 722 : Fraction Note::playTicksFraction() const
1304 : {
1305 722 : if (!m_tieBack && !m_tieFor && chord()) {
1306 670 : return chord()->actualTicks();
1307 : }
1308 52 : return lastTiedNote()->chord()->endTick() - firstTiedNote()->chord()->tick();
1309 : }
1310 :
1311 : //---------------------------------------------------------
1312 : // addSpanner
1313 : //---------------------------------------------------------
1314 :
1315 391 : void Note::addSpanner(Spanner* l)
1316 : {
1317 391 : EngravingItem* e = l->endElement();
1318 391 : if (e && e->isNote()) {
1319 372 : Note* note = toNote(e);
1320 372 : note->addSpannerBack(l);
1321 372 : bool isNoteAnchoredTextLine = l->isNoteLine() && toNoteLine(l)->enforceMinLength();
1322 372 : if (l->isGlissando() || l->isGuitarBend() || isNoteAnchoredTextLine) {
1323 372 : note->chord()->setEndsNoteAnchoredLine(true);
1324 : }
1325 : }
1326 391 : addSpannerFor(l);
1327 391 : }
1328 :
1329 : //---------------------------------------------------------
1330 : // removeSpanner
1331 : //---------------------------------------------------------
1332 :
1333 0 : void Note::removeSpanner(Spanner* l)
1334 : {
1335 0 : Note* e = toNote(l->endElement());
1336 0 : if (e && e->isNote()) {
1337 0 : if (!e->removeSpannerBack(l)) {
1338 0 : LOGD("Note::removeSpanner(%p): cannot remove spannerBack %s %p", this, l->typeName(), l);
1339 : // abort();
1340 : }
1341 0 : if (l->isGlissando()) {
1342 0 : e->chord()->updateEndsNoteAnchoredLine();
1343 : }
1344 : }
1345 0 : if (!removeSpannerFor(l)) {
1346 0 : LOGD("Note(%p): cannot remove spannerFor %s %p", this, l->typeName(), l);
1347 : // abort();
1348 : }
1349 0 : }
1350 :
1351 : //---------------------------------------------------------
1352 : // add
1353 : //---------------------------------------------------------
1354 :
1355 7123 : void Note::add(EngravingItem* e)
1356 : {
1357 7123 : if (e->ownershipParent() != this) {
1358 349 : e->setOwnershipParent(this);
1359 : }
1360 7123 : e->setTrack(track());
1361 :
1362 7123 : switch (e->type()) {
1363 711 : case ElementType::NOTEDOT:
1364 711 : m_dots.push_back(toNoteDot(e));
1365 711 : break;
1366 338 : case ElementType::BEND:
1367 : case ElementType::FINGERING:
1368 : case ElementType::IMAGE:
1369 : case ElementType::TEXT:
1370 338 : m_el.push_back(e);
1371 338 : break;
1372 32 : case ElementType::SYMBOL: {
1373 32 : m_el.push_back(e);
1374 32 : } break;
1375 19 : case ElementType::LAISSEZ_VIB: {
1376 19 : LaissezVib* lv = toLaissezVib(e);
1377 19 : lv->setStartNote(this);
1378 19 : lv->setTick(lv->startNote()->tick());
1379 19 : setTieFor(lv);
1380 19 : break;
1381 : }
1382 75 : case ElementType::PARTIAL_TIE: {
1383 75 : PartialTie* pt = toPartialTie(e);
1384 75 : pt->setTick(tick());
1385 75 : if (pt->isOutgoing()) {
1386 24 : pt->setStartNote(this);
1387 24 : setTieFor(pt);
1388 : } else {
1389 51 : pt->setEndNote(this);
1390 51 : setTieBack(pt);
1391 : }
1392 75 : break;
1393 : }
1394 491 : case ElementType::TIE: {
1395 491 : Tie* tie = toTie(e);
1396 491 : tie->setStartNote(this);
1397 491 : tie->setTick(tie->startNote()->tick());
1398 491 : setTieFor(tie);
1399 491 : if (tie->endNote()) {
1400 375 : tie->endNote()->setTieBack(tie);
1401 : }
1402 : }
1403 491 : break;
1404 5066 : case ElementType::ACCIDENTAL:
1405 5066 : m_accidental = toAccidental(e);
1406 5066 : m_centOffset = Accidental::subtype2centOffset(toAccidental(e)->accidentalType());
1407 5066 : break;
1408 391 : case ElementType::TEXTLINE:
1409 : case ElementType::NOTELINE:
1410 : case ElementType::GLISSANDO:
1411 : case ElementType::GUITAR_BEND:
1412 391 : addSpanner(toSpanner(e));
1413 391 : break;
1414 0 : default:
1415 0 : EngravingItem::add(e);
1416 0 : break;
1417 : }
1418 7123 : triggerLayout();
1419 :
1420 7123 : e->added();
1421 7123 : }
1422 :
1423 : //---------------------------------------------------------
1424 : // remove
1425 : //---------------------------------------------------------
1426 :
1427 2874 : void Note::remove(EngravingItem* e)
1428 : {
1429 2874 : switch (e->type()) {
1430 12 : case ElementType::NOTEDOT:
1431 12 : m_dots.pop_back();
1432 12 : break;
1433 :
1434 24 : case ElementType::BEND:
1435 : case ElementType::TEXT:
1436 : case ElementType::IMAGE:
1437 : case ElementType::FINGERING:
1438 24 : if (!m_el.remove(e)) {
1439 0 : LOGD("Note::remove(): cannot find %s", e->typeName());
1440 : }
1441 24 : break;
1442 12 : case ElementType::SYMBOL:
1443 12 : if (!m_el.remove(e)) {
1444 0 : LOGD("Note::remove(): cannot find %s", e->typeName());
1445 : }
1446 12 : break;
1447 39 : case ElementType::PARTIAL_TIE: {
1448 39 : PartialTie* pt = toPartialTie(e);
1449 39 : assert((pt->isOutgoing() ? pt->startNote() : pt->endNote()) == this);
1450 39 : if (pt->isOutgoing()) {
1451 10 : setTieFor(nullptr);
1452 : } else {
1453 29 : setTieBack(nullptr);
1454 29 : pt->setJumpPoint(nullptr);
1455 : }
1456 39 : break;
1457 : }
1458 1006 : case ElementType::LAISSEZ_VIB:
1459 : case ElementType::TIE: {
1460 1006 : Tie* tie = toTie(e);
1461 1006 : assert(tie->startNote() == this);
1462 1006 : setTieFor(nullptr);
1463 1006 : tie->setJumpPoint(nullptr);
1464 1006 : if (tie->endNote()) {
1465 991 : tie->endNote()->setTieBack(0);
1466 : }
1467 : }
1468 1006 : break;
1469 :
1470 1781 : case ElementType::ACCIDENTAL:
1471 1781 : m_accidental = 0;
1472 1781 : m_centOffset = 0;
1473 1781 : break;
1474 :
1475 0 : case ElementType::TEXTLINE:
1476 : case ElementType::NOTELINE:
1477 : case ElementType::GLISSANDO:
1478 : case ElementType::GUITAR_BEND:
1479 0 : removeSpanner(toSpanner(e));
1480 0 : break;
1481 :
1482 0 : default:
1483 0 : EngravingItem::remove(e);
1484 0 : break;
1485 : }
1486 2874 : triggerLayout();
1487 2874 : e->removed();
1488 2874 : }
1489 :
1490 : //---------------------------------------------------------
1491 : // isNoteName
1492 : //---------------------------------------------------------
1493 :
1494 58698 : bool Note::isNoteName() const
1495 : {
1496 58698 : if (chord() && chord()->staff()) {
1497 58698 : const Staff* st = staff();
1498 58698 : NoteHeadScheme s = m_headScheme;
1499 58698 : if (s == NoteHeadScheme::HEAD_AUTO) {
1500 58561 : s = st->staffTypeForElement(this)->noteHeadScheme();
1501 : }
1502 58606 : return s == NoteHeadScheme::HEAD_PITCHNAME || s == NoteHeadScheme::HEAD_PITCHNAME_GERMAN
1503 58593 : || s == NoteHeadScheme::HEAD_PITCHNAME_NO_ACCIDENTALS || s == NoteHeadScheme::HEAD_PITCHNAME_GERMAN_NO_ACCIDENTALS
1504 117304 : || s == NoteHeadScheme::HEAD_SOLFEGE || s == NoteHeadScheme::HEAD_SOLFEGE_FIXED;
1505 : }
1506 0 : return false;
1507 : }
1508 :
1509 1028 : void Note::updateFrettingForTiesAndBends()
1510 : {
1511 1028 : Note* prevNote = nullptr;
1512 1028 : if (m_tieBack) {
1513 37 : prevNote = m_tieBack->startNote();
1514 : } else {
1515 991 : GuitarBend* bend = bendBack();
1516 991 : if (bend) {
1517 25 : prevNote = bend->startNote();
1518 : }
1519 : }
1520 :
1521 1028 : if (!prevNote) {
1522 966 : return;
1523 : }
1524 :
1525 62 : setString(prevNote->string());
1526 62 : setFret(prevNote->fret());
1527 : }
1528 :
1529 26548 : bool Note::shouldHideFret() const
1530 : {
1531 26548 : if (!tieBack() || shouldForceShowFret() || !staffType()->isTabStaff()) {
1532 26229 : return false;
1533 : }
1534 :
1535 319 : if (isContinuationOfBend() && !rtick().isZero()) {
1536 10 : return true;
1537 : }
1538 :
1539 309 : ShowTiedFret showTiedFret = style().value(Sid::tabShowTiedFret).value<ShowTiedFret>();
1540 309 : if (showTiedFret == ShowTiedFret::TIE_AND_FRET) {
1541 309 : return false;
1542 : }
1543 :
1544 0 : ParenthesizeTiedFret parenthTiedFret = style().value(Sid::tabParenthesizeTiedFret).value<ParenthesizeTiedFret>();
1545 0 : if (parenthTiedFret == ParenthesizeTiedFret::NEVER || !rtick().isZero()) {
1546 0 : return true;
1547 : }
1548 :
1549 0 : if (parenthTiedFret == ParenthesizeTiedFret::START_OF_MEASURE) {
1550 0 : return false;
1551 : }
1552 :
1553 0 : const Measure* measure = findMeasure();
1554 0 : bool isStartOfSystem = measure && measure->system() && measure->isFirstInSystem();
1555 :
1556 0 : return !isStartOfSystem;
1557 : }
1558 :
1559 13005 : bool Note::shouldForceShowFret() const
1560 : {
1561 13005 : if (!style().styleB(Sid::parenthesizeTiedFretIfArticulation)) {
1562 0 : return false;
1563 : }
1564 :
1565 13005 : Chord* ch = chord();
1566 13005 : if (!ch) {
1567 0 : return false;
1568 : }
1569 :
1570 12933 : auto hasTremoloBar = [&] () {
1571 13325 : for (EngravingItem* item : ch->segment()->annotations()) {
1572 392 : if (item && item->isTremoloBar() && item->track() == track()) {
1573 0 : return true;
1574 : }
1575 : }
1576 12933 : return false;
1577 13005 : };
1578 :
1579 12933 : auto hasVibratoLine = [&] () {
1580 12933 : auto spanners = score()->spannerMap().findOverlapping(tick().ticks(), ch->endTick().ticks());
1581 15548 : for (auto interval : spanners) {
1582 2623 : Spanner* sp = interval.value;
1583 2623 : if (sp->isVibrato() && sp->startElement() == ch) {
1584 8 : return true;
1585 : }
1586 : }
1587 12925 : return false;
1588 12933 : };
1589 :
1590 13005 : const GuitarBend* bendF = bendFor();
1591 13005 : bool startUnconnectedBend = bendF && !bendF->findPrecedingBend();
1592 13005 : bool startsNonBendSpanner = !spannerFor().empty() && !bendF;
1593 :
1594 13005 : bool chordHasDip = false;
1595 31842 : for (Note* note : chord()->notes()) {
1596 18955 : for (Spanner* sp : note->spannerFor()) {
1597 118 : if (sp->isGuitarBend() && toGuitarBend(sp)->bendType() == GuitarBendType::DIP) {
1598 8 : chordHasDip = true;
1599 8 : break;
1600 : }
1601 : }
1602 18845 : if (chordHasDip) {
1603 8 : break;
1604 : }
1605 : }
1606 :
1607 25998 : return !ch->articulations().empty() || ch->chordLine() || startsNonBendSpanner || startUnconnectedBend || hasTremoloBar()
1608 25998 : || hasVibratoLine() || chordHasDip;
1609 : }
1610 :
1611 5292 : void Note::setVisible(bool v)
1612 : {
1613 5292 : EngravingItem::setVisible(v);
1614 5292 : if (!chord() || chord()->noteParentheses().empty()) {
1615 5280 : return;
1616 : }
1617 :
1618 12 : const NoteParenthesisInfo* noteParenInfo = chord()->findNoteParenthesisInfo(this);
1619 :
1620 12 : if (!noteParenInfo) {
1621 0 : return;
1622 : }
1623 :
1624 12 : const std::vector<Note*>& notes = noteParenInfo->notes();
1625 12 : bool visible = false;
1626 22 : for (const Note* note : notes) {
1627 12 : if (note->visible()) {
1628 2 : visible = true;
1629 2 : break;
1630 : }
1631 : }
1632 :
1633 12 : if (noteParenInfo->leftParen()) {
1634 12 : noteParenInfo->leftParen()->setVisible(visible);
1635 : }
1636 12 : if (noteParenInfo->rightParen()) {
1637 12 : noteParenInfo->rightParen()->setVisible(visible);
1638 : }
1639 : }
1640 :
1641 174 : bool Note::isExactUnison(Note* other)
1642 : {
1643 174 : return other->pitch() == m_pitch && other->tpc() == tpc();
1644 : }
1645 :
1646 17619 : void Note::setupAfterRead(const Fraction& ctxTick, bool pasteMode)
1647 : {
1648 : // ensure sane values:
1649 17619 : m_pitch = std::clamp(m_pitch, 0, 127);
1650 :
1651 17619 : if (!tpcIsValid(m_tpc[0]) && !tpcIsValid(m_tpc[1])) {
1652 0 : Key key = (staff() && chord()) ? staff()->key(chord()->tick()) : Key::C;
1653 0 : int tpc = pitch2tpc(m_pitch, key, Prefer::NEAREST);
1654 0 : if (concertPitch()) {
1655 0 : m_tpc[0] = tpc;
1656 : } else {
1657 0 : m_tpc[1] = tpc;
1658 : }
1659 : }
1660 17619 : if (!(tpcIsValid(m_tpc[0]) && tpcIsValid(m_tpc[1]))) {
1661 0 : Fraction tick = chord() ? chord()->tick() : Fraction(-1, 1);
1662 0 : Interval v = staff() ? staff()->transpose(tick) : Interval();
1663 0 : if (tpcIsValid(m_tpc[0])) {
1664 0 : v.flip();
1665 0 : if (v.isZero()) {
1666 0 : m_tpc[1] = m_tpc[0];
1667 : } else {
1668 0 : m_tpc[1] = Transpose::transposeTpc(m_tpc[0], v, true);
1669 : }
1670 : } else {
1671 0 : if (v.isZero()) {
1672 0 : m_tpc[0] = m_tpc[1];
1673 : } else {
1674 0 : m_tpc[0] = Transpose::transposeTpc(m_tpc[1], v, true);
1675 : }
1676 : }
1677 : }
1678 :
1679 : // check consistency of pitch, tpc1, tpc2, and transposition
1680 : // see note in InstrumentChange::read() about a known case of tpc corruption produced in 2.0.x
1681 : // but since there are other causes of tpc corruption (eg, https://musescore.org/en/node/74746)
1682 : // including perhaps some we don't know about yet,
1683 : // we will attempt to fix some problems here regardless of version
1684 :
1685 17619 : if (staff() && !staff()->isDrumStaff(ctxTick) && !pasteMode && !MScore::testMode) {
1686 0 : int tpc1Pitch = (tpc2pitch(m_tpc[0]) + 12) % 12;
1687 0 : int tpc2Pitch = (tpc2pitch(m_tpc[1]) + 12) % 12;
1688 0 : int soundingPitch = m_pitch % 12;
1689 0 : if (tpc1Pitch != soundingPitch) {
1690 0 : LOGD("bad tpc1 - soundingPitch = %d, tpc1 = %d", soundingPitch, tpc1Pitch);
1691 0 : m_pitch += tpc1Pitch - soundingPitch;
1692 : }
1693 0 : if (staff()) {
1694 0 : Interval v = staff()->transpose(ctxTick);
1695 0 : int writtenPitch = (m_pitch - v.chromatic) % 12;
1696 0 : if (tpc2Pitch != writtenPitch) {
1697 0 : LOGD("bad tpc2 - writtenPitch = %d, tpc2 = %d", writtenPitch, tpc2Pitch);
1698 0 : if (concertPitch()) {
1699 : // assume we want to keep sounding pitch
1700 : // so fix written pitch (tpc only)
1701 0 : v.flip();
1702 0 : m_tpc[1] = Transpose::transposeTpc(m_tpc[0], v, true);
1703 : } else {
1704 : // assume we want to keep written pitch
1705 : // so fix sounding pitch (both tpc and pitch)
1706 0 : m_tpc[0] = Transpose::transposeTpc(m_tpc[1], v, true);
1707 0 : m_pitch += tpc2Pitch - writtenPitch;
1708 : }
1709 : }
1710 : }
1711 : }
1712 :
1713 17619 : const StaffType* st = staffType();
1714 17619 : if (st && st->isTabStaff() && st->fretUseTextStyle() && color() == configuration()->defaultColor()) {
1715 49 : setColor(propertyDefault(Pid::COLOR).value<Color>());
1716 : }
1717 17619 : }
1718 :
1719 : //---------------------------------------------------------
1720 : // transposition
1721 : //---------------------------------------------------------
1722 :
1723 212207 : int Note::transposition() const
1724 : {
1725 212207 : Fraction tick = chord() ? chord()->tick() : Fraction(-1, 1);
1726 212207 : Part* origin = originPart();
1727 424414 : return origin ? origin->instrument(tick)->transpose().chromatic : 0;
1728 : }
1729 :
1730 : //---------------------------------------------------------
1731 : // NoteEditData
1732 : //---------------------------------------------------------
1733 :
1734 : class NoteEditData : public ElementEditData
1735 : {
1736 0 : OBJECT_ALLOCATOR(engraving, NoteEditData)
1737 : public:
1738 : enum EditMode : unsigned char {
1739 : EditMode_ChangePitch = 0,
1740 : EditMode_AddSpacing,
1741 : EditMode_Undefined,
1742 : };
1743 :
1744 : int line = 0;
1745 : int string = 0;
1746 : EditMode mode = EditMode_Undefined;
1747 : PointF delta;
1748 :
1749 0 : virtual EditDataType type() override { return EditDataType::NoteEditData; }
1750 :
1751 : static constexpr double MODE_TRANSITION_LIMIT_DEGREES = 15.0;
1752 :
1753 0 : static inline EditMode editModeByDragDirection(const double& deltaX, const double& deltaY)
1754 : {
1755 0 : double x = std::abs(deltaX);
1756 0 : double y = std::abs(deltaY);
1757 :
1758 0 : PointF normalizedVector(x, y);
1759 :
1760 0 : normalizedVector.normalize();
1761 :
1762 0 : float radians = PointF::dotProduct(normalizedVector, PointF(1, 0));
1763 :
1764 0 : double degrees = (std::acos(radians) * 180.0) / M_PI;
1765 :
1766 0 : LOGD() << "NOTE DRAG DEGREES " << degrees;
1767 :
1768 0 : if (degrees >= MODE_TRANSITION_LIMIT_DEGREES) {
1769 0 : return NoteEditData::EditMode_ChangePitch;
1770 : } else {
1771 0 : return NoteEditData::EditMode_AddSpacing;
1772 : }
1773 : }
1774 : };
1775 :
1776 : //---------------------------------------------------------
1777 : // acceptDrop
1778 : //---------------------------------------------------------
1779 :
1780 18 : bool Note::acceptDrop(EditData& data) const
1781 : {
1782 18 : EngravingItem* e = data.dropElement;
1783 18 : switch (e->type()) {
1784 0 : case ElementType::GLISSANDO:
1785 : case ElementType::GUITAR_BEND:
1786 0 : for (auto ee : m_spannerFor) {
1787 0 : if (ee->isGlissando() || ee->isGuitarBend()) {
1788 0 : return false;
1789 : }
1790 : }
1791 0 : return true;
1792 0 : case ElementType::FINGERING:
1793 0 : return staff()->isTabStaff(tick()) ? staffType()->showTabFingering() : true;
1794 10 : case ElementType::ARTICULATION:
1795 : case ElementType::ORNAMENT:
1796 : case ElementType::TAPPING:
1797 : case ElementType::CHORDLINE:
1798 : case ElementType::TEXT:
1799 : case ElementType::ACCIDENTAL:
1800 : case ElementType::ARPEGGIO:
1801 : case ElementType::CHORD_BRACKET:
1802 : case ElementType::NOTEHEAD:
1803 : case ElementType::NOTE:
1804 : case ElementType::TREMOLO_SINGLECHORD:
1805 : case ElementType::TREMOLO_TWOCHORD:
1806 : case ElementType::IMAGE:
1807 : case ElementType::CHORD:
1808 : case ElementType::SYMBOL:
1809 : case ElementType::BAR_LINE:
1810 : case ElementType::STICKING:
1811 : case ElementType::BEND:
1812 : case ElementType::FIGURED_BASS:
1813 : case ElementType::LYRICS:
1814 10 : return true;
1815 0 : case ElementType::BAGPIPE_EMBELLISHMENT:
1816 0 : return noteType() == NoteType::NORMAL;
1817 0 : case ElementType::ACTION_ICON: {
1818 0 : switch (toActionIcon(e)->actionType()) {
1819 0 : case ActionIconType::ACCIACCATURA:
1820 : case ActionIconType::APPOGGIATURA:
1821 : case ActionIconType::GRACE4:
1822 : case ActionIconType::GRACE16:
1823 : case ActionIconType::GRACE32:
1824 : case ActionIconType::GRACE8_AFTER:
1825 : case ActionIconType::GRACE16_AFTER:
1826 : case ActionIconType::GRACE32_AFTER:
1827 : case ActionIconType::PARENTHESES:
1828 : case ActionIconType::STANDARD_BEND:
1829 : case ActionIconType::PRE_BEND:
1830 : case ActionIconType::GRACE_NOTE_BEND:
1831 : case ActionIconType::SLIGHT_BEND:
1832 : case ActionIconType::DIVE:
1833 : case ActionIconType::PRE_DIVE:
1834 : case ActionIconType::DIP:
1835 : case ActionIconType::SCOOP:
1836 : case ActionIconType::NOTE_ANCHORED_LINE:
1837 0 : return true;
1838 0 : default: break;
1839 : }
1840 0 : break;
1841 : }
1842 8 : default:
1843 8 : if (e->isSpanner()) {
1844 0 : return e->type() != ElementType::TIE && e->type() != ElementType::PARTIAL_TIE;
1845 : }
1846 8 : break;
1847 : }
1848 8 : return toChordRest(chord())->acceptDrop(data);
1849 : }
1850 :
1851 : //---------------------------------------------------------
1852 : // drop
1853 : //---------------------------------------------------------
1854 :
1855 48 : EngravingItem* Note::drop(Transaction& tx, EditData& data)
1856 : {
1857 48 : EngravingItem* e = data.dropElement;
1858 :
1859 48 : const Staff* st = staff();
1860 48 : bool isTablature = st->isTabStaff(tick());
1861 48 : bool tabFingering = st->staffTypeForElement(this)->showTabFingering();
1862 48 : Chord* ch = chord();
1863 :
1864 48 : switch (e->type()) {
1865 6 : case ElementType::SYMBOL:
1866 : case ElementType::IMAGE:
1867 6 : e->setOwnershipParent(this);
1868 6 : score()->undoAddElement(e);
1869 6 : return e;
1870 :
1871 3 : case ElementType::FINGERING:
1872 3 : if (!isTablature || tabFingering) {
1873 3 : e->setOwnershipParent(this);
1874 3 : score()->undoAddElement(e);
1875 3 : return e;
1876 : } else {
1877 0 : delete e;
1878 : }
1879 0 : return 0;
1880 :
1881 0 : case ElementType::LYRICS:
1882 0 : e->setOwnershipParent(ch);
1883 0 : e->setTrack(track());
1884 0 : score()->undoAddElement(e);
1885 0 : return e;
1886 :
1887 0 : case ElementType::ACCIDENTAL:
1888 0 : EditNote::changeAccidental(score(), this, toAccidental(e)->accidentalType());
1889 0 : break;
1890 :
1891 0 : case ElementType::BEND:
1892 0 : e->setOwnershipParent(this);
1893 0 : e->setTrack(track());
1894 0 : score()->undoAddElement(e);
1895 0 : return e;
1896 :
1897 0 : case ElementType::NOTEHEAD:
1898 : {
1899 0 : NoteHead* s = toNoteHead(e);
1900 0 : NoteHeadGroup group = s->headGroup();
1901 0 : if (group == NoteHeadGroup::HEAD_INVALID) {
1902 0 : LOGD("unknown notehead");
1903 0 : group = NoteHeadGroup::HEAD_NORMAL;
1904 : }
1905 0 : delete s;
1906 :
1907 0 : if (group != m_headGroup) {
1908 0 : if (links()) {
1909 0 : for (EngravingObject* se : *links()) {
1910 0 : se->undoChangeProperty(Pid::HEAD_GROUP, group);
1911 0 : Note* note = toNote(se);
1912 0 : if (note->staff() && !note->staff()->isDrumStaff(ch->tick())) {
1913 0 : se->undoChangeProperty(Pid::DEAD, group == NoteHeadGroup::HEAD_CROSS);
1914 : }
1915 : }
1916 : } else {
1917 0 : undoChangeProperty(Pid::HEAD_GROUP, group);
1918 0 : if (!staff()->isDrumStaff(ch->tick())) {
1919 0 : undoChangeProperty(Pid::DEAD, group == NoteHeadGroup::HEAD_CROSS);
1920 : }
1921 : }
1922 : }
1923 : }
1924 0 : break;
1925 :
1926 0 : case ElementType::ACTION_ICON:
1927 : {
1928 0 : switch (toActionIcon(e)->actionType()) {
1929 0 : case ActionIconType::ACCIACCATURA:
1930 : {
1931 0 : Note* note = score()->setGraceNote(ch, pitch(), NoteType::ACCIACCATURA, Constants::DIVISION / 2);
1932 0 : score()->select(note, SelectType::SINGLE, 0);
1933 0 : break;
1934 : }
1935 0 : case ActionIconType::APPOGGIATURA:
1936 : {
1937 0 : Note* note = score()->setGraceNote(ch, pitch(), NoteType::APPOGGIATURA, Constants::DIVISION / 2);
1938 0 : score()->select(note, SelectType::SINGLE, 0);
1939 0 : break;
1940 : }
1941 0 : case ActionIconType::GRACE4:
1942 : {
1943 0 : Note* note = score()->setGraceNote(ch, pitch(), NoteType::GRACE4, Constants::DIVISION);
1944 0 : score()->select(note, SelectType::SINGLE, 0);
1945 0 : break;
1946 : }
1947 0 : case ActionIconType::GRACE16:
1948 : {
1949 0 : Note* note = score()->setGraceNote(ch, pitch(), NoteType::GRACE16, Constants::DIVISION / 4);
1950 0 : score()->select(note, SelectType::SINGLE, 0);
1951 0 : break;
1952 : }
1953 0 : case ActionIconType::GRACE32:
1954 : {
1955 0 : Note* note = score()->setGraceNote(ch, pitch(), NoteType::GRACE32, Constants::DIVISION / 8);
1956 0 : score()->select(note, SelectType::SINGLE, 0);
1957 0 : break;
1958 : }
1959 0 : case ActionIconType::GRACE8_AFTER:
1960 : {
1961 0 : Note* note = score()->setGraceNote(ch, pitch(), NoteType::GRACE8_AFTER, Constants::DIVISION / 2);
1962 0 : score()->select(note, SelectType::SINGLE, 0);
1963 0 : break;
1964 : }
1965 0 : case ActionIconType::GRACE16_AFTER:
1966 : {
1967 0 : Note* note = score()->setGraceNote(ch, pitch(), NoteType::GRACE16_AFTER, Constants::DIVISION / 4);
1968 0 : score()->select(note, SelectType::SINGLE, 0);
1969 0 : break;
1970 : }
1971 0 : case ActionIconType::GRACE32_AFTER:
1972 : {
1973 0 : Note* note = score()->setGraceNote(ch, pitch(), NoteType::GRACE32_AFTER, Constants::DIVISION / 8);
1974 0 : score()->select(note, SelectType::SINGLE, 0);
1975 0 : break;
1976 : }
1977 :
1978 0 : case ActionIconType::PARENTHESES: {
1979 0 : EditParentheses::addParenthesesToNotes(tx, { this });
1980 0 : break;
1981 : }
1982 0 : case ActionIconType::STANDARD_BEND:
1983 : case ActionIconType::SLIGHT_BEND:
1984 : case ActionIconType::DIVE:
1985 : case ActionIconType::DIP:
1986 : case ActionIconType::SCOOP:
1987 0 : score()->addGuitarBend(GuitarBend::bendTypeFromActionIcon(toActionIcon(e)->actionType()), this);
1988 0 : break;
1989 0 : case ActionIconType::PRE_BEND:
1990 : case ActionIconType::GRACE_NOTE_BEND:
1991 : case ActionIconType::PRE_DIVE:
1992 : {
1993 0 : GuitarBendType type = GuitarBend::bendTypeFromActionIcon(toActionIcon(e)->actionType());
1994 0 : GuitarBend* guitarBend = score()->addGuitarBend(type, this);
1995 0 : if (!guitarBend) {
1996 0 : break;
1997 : }
1998 0 : Note* note = guitarBend->startNote();
1999 0 : IF_ASSERT_FAILED(note) {
2000 0 : LOGE() << "not valid start note of the bend";
2001 0 : break;
2002 : }
2003 :
2004 0 : score()->select(note, SelectType::SINGLE, 0);
2005 0 : break;
2006 : }
2007 0 : case mu::engraving::ActionIconType::NOTE_ANCHORED_LINE:
2008 0 : score()->addNoteLine();
2009 0 : default:
2010 0 : break;
2011 : }
2012 0 : return ch->drop(tx, data);
2013 : }
2014 :
2015 0 : case ElementType::GUITAR_BEND:
2016 : {
2017 0 : GuitarBend* newGuitarBend = score()->addGuitarBend(toGuitarBend(e)->bendType(), this);
2018 0 : delete e;
2019 0 : return newGuitarBend;
2020 : }
2021 :
2022 0 : case ElementType::BAGPIPE_EMBELLISHMENT:
2023 : {
2024 0 : BagpipeEmbellishment* b = toBagpipeEmbellishment(e);
2025 0 : BagpipeNoteList nl = b->resolveNoteList();
2026 : // add grace notes in reverse order, as setGraceNote adds a grace note
2027 : // before the current note
2028 0 : for (int i = static_cast<int>(nl.size()) - 1; i >= 0; --i) {
2029 0 : int p = BagpipeEmbellishment::BAGPIPE_NOTEINFO_LIST[nl.at(i)].pitch;
2030 0 : score()->setGraceNote(ch, p, NoteType::GRACE32, Constants::DIVISION / 8);
2031 : }
2032 0 : }
2033 0 : delete e;
2034 0 : break;
2035 :
2036 14 : case ElementType::NOTE:
2037 : {
2038 : // calculate correct transposed tpc
2039 14 : Note* n = toNote(e);
2040 14 : const Segment* segment = ch->segment();
2041 14 : Interval v = staff()->transpose(ch->tick());
2042 14 : v.flip();
2043 14 : n->setTpc2(Transpose::transposeTpc(n->tpc1(), v, true));
2044 : // replace this note with new note
2045 14 : n->setOwnershipParent(ch);
2046 14 : if (this->tieBack()) {
2047 4 : n->setTieBack(this->tieBack());
2048 4 : n->tieBack()->setEndNote(n);
2049 4 : this->setTieBack(nullptr);
2050 : }
2051 : // Set correct stem direction for drum staves
2052 14 : const StaffGroup staffGroup = st->staffType(segment->tick())->group();
2053 14 : DirectionV stemDirection = DirectionV::AUTO;
2054 14 : if (staffGroup == StaffGroup::PERCUSSION) {
2055 2 : const Drumset* ds = originPart()->instrument(segment->tick())->drumset();
2056 2 : DO_ASSERT(ds);
2057 :
2058 2 : if (ds) {
2059 2 : stemDirection = ds->stemDirection(n->noteVal().pitch);
2060 : }
2061 : }
2062 14 : ch->setStemDirection(stemDirection);
2063 :
2064 14 : score()->undoRemoveElement(this);
2065 14 : score()->undoAddElement(n);
2066 14 : return n;
2067 : }
2068 : break;
2069 :
2070 11 : case ElementType::GLISSANDO:
2071 : {
2072 11 : for (auto ee : m_spannerFor) {
2073 0 : if (ee->isGlissando()) {
2074 0 : LOGD("there is already a glissando");
2075 0 : delete e;
2076 0 : return 0;
2077 : }
2078 : }
2079 :
2080 11 : Glissando* gliss = toGlissando(e);
2081 11 : EngravingItem* endEl = gliss->endElement();
2082 11 : Note* finalNote = endEl && endEl->isNote() ? toNote(endEl) : SLine::guessFinalNote(this);
2083 11 : if (finalNote) {
2084 : // init glissando data
2085 11 : gliss->setStartElement(this);
2086 11 : gliss->setEndElement(finalNote);
2087 11 : gliss->setTick(ch->tick());
2088 11 : gliss->setTick2(finalNote->chord()->tick());
2089 11 : gliss->setTrack(track());
2090 11 : gliss->setTrack2(finalNote->track());
2091 : // in TAB, use straight line with no text
2092 11 : if (staff()->isTabStaff(finalNote->chord()->tick())) {
2093 0 : gliss->setGlissandoType(GlissandoType::STRAIGHT);
2094 0 : gliss->setShowText(false);
2095 : }
2096 11 : gliss->setOwnershipParent(this);
2097 :
2098 11 : const Sid styleId = gliss->getPropertyStyle(Pid::GLISS_STYLE);
2099 11 : if (gliss->isStyled(Pid::GLISS_STYLE) && score()->style().isDefault(styleId)) {
2100 11 : const GlissandoStyle instrumentStyle = originPart()->instrument(gliss->tick())->glissandoStyle();
2101 11 : if (instrumentStyle != gliss->glissandoStyle()) {
2102 0 : gliss->setGlissandoStyle(instrumentStyle);
2103 0 : gliss->setPropertyFlags(Pid::GLISS_STYLE, PropertyFlags::UNSTYLED);
2104 : }
2105 : }
2106 :
2107 11 : score()->undoAddElement(e);
2108 : } else {
2109 0 : LOGD("no segment for second note of glissando found");
2110 0 : delete e;
2111 0 : return 0;
2112 : }
2113 : }
2114 11 : break;
2115 :
2116 0 : case ElementType::CHORD:
2117 : {
2118 0 : Chord* c = toChord(e);
2119 0 : Note* n = c->upNote();
2120 0 : DirectionV dir = c->stemDirection();
2121 0 : track_idx_t t = track(); // (staff2track(staffIdx()) + n->voice());
2122 0 : score()->select(0, SelectType::SINGLE, 0);
2123 0 : NoteVal nval;
2124 0 : nval.pitch = n->pitch();
2125 0 : nval.headGroup = n->headGroup();
2126 0 : const ChordRest* cr = nullptr;
2127 0 : if (data.modifiers & ShiftModifier) {
2128 : // add note to chord
2129 0 : NoteInput::addNote(tx, score(), ch, nval);
2130 : } else {
2131 : // replace current chord
2132 0 : Segment* seg = score()->setNoteRest(ch->segment(), t, nval,
2133 0 : score()->inputState().duration().fraction(), dir);
2134 0 : cr = seg ? toChordRest(seg->element(t)) : nullptr;
2135 : }
2136 0 : if (cr) {
2137 0 : NoteInput::nextInputPos(tx, score(), cr, false);
2138 : }
2139 0 : delete e;
2140 : }
2141 0 : break;
2142 :
2143 3 : case ElementType::CHORDLINE:
2144 3 : toChordLine(e)->setNote(this);
2145 3 : return ch->drop(tx, data);
2146 :
2147 11 : default:
2148 : Spanner* spanner;
2149 11 : if (e->isSpanner() && (spanner = toSpanner(e))->anchor() == Spanner::Anchor::NOTE) {
2150 0 : spanner->setOwnershipParent(this);
2151 0 : spanner->setStartElement(this);
2152 0 : spanner->setTick(tick());
2153 0 : spanner->setTrack(track());
2154 0 : spanner->setTrack2(track());
2155 0 : spanner->computeEndElement();
2156 0 : score()->undoAddElement(spanner);
2157 0 : return e;
2158 : }
2159 11 : return ch->drop(tx, data);
2160 : }
2161 11 : return 0;
2162 : }
2163 :
2164 : //---------------------------------------------------------
2165 : // dotIsUp
2166 : //---------------------------------------------------------
2167 :
2168 0 : bool Note::dotIsUp() const
2169 : {
2170 0 : if (m_dots.empty()) {
2171 0 : return true;
2172 : }
2173 0 : if (m_userDotPosition == DirectionV::AUTO) {
2174 0 : return m_dots[0]->y() < spatium() * .1;
2175 : } else {
2176 0 : return m_userDotPosition == DirectionV::UP;
2177 : }
2178 : }
2179 :
2180 52491 : static bool hasAlteredUnison(Note* note)
2181 : {
2182 52491 : const auto& chordNotes = note->chord()->notes();
2183 52491 : AccidentalVal accVal = tpc2alter(note->tpc());
2184 52491 : int absLine = absStep(note->tpc(), note->epitch());
2185 52491 : return std::find_if(chordNotes.begin(), chordNotes.end(), [note, accVal, absLine](Note* n) {
2186 89303 : return n != note && !n->hidden() && absStep(n->tpc(), n->epitch()) == absLine && tpc2alter(n->tpc()) != accVal;
2187 104982 : }) != chordNotes.end();
2188 : }
2189 :
2190 : //---------------------------------------------------------
2191 : // updateAccidental
2192 : // set _accidental and _line depending on tpc
2193 : //---------------------------------------------------------
2194 :
2195 56332 : void Note::updateAccidental(AccidentalState* as)
2196 : {
2197 56332 : if (deadNote() && configuration()->keepDeadNotesUnchangedOnTranspose()) {
2198 0 : if (m_accidental) {
2199 0 : score()->undoRemoveElement(m_accidental);
2200 : }
2201 0 : int eAbsLine = absStep(tpc(), epitch());
2202 0 : as->setAccidentalVal(eAbsLine, tpc2alter(tpc()), false);
2203 0 : updateRelLine(eAbsLine, true);
2204 0 : return;
2205 : }
2206 :
2207 56332 : int absLine = absStep(tpc(), epitch());
2208 :
2209 : // Ensure m_centOffset and microtonal accidental match (they can mismatch when switching from TAB)
2210 56332 : if (muse::RealIsNull(m_centOffset)) {
2211 56249 : if (m_accidental && !muse::RealIsNull(Accidental::subtype2centOffset(m_accidental->accidentalType()))) {
2212 0 : score()->undoRemoveElement(m_accidental);
2213 : }
2214 : } else {
2215 83 : if (m_accidental) {
2216 83 : bool correct = muse::RealIsEqual(Accidental::subtype2centOffset(m_accidental->accidentalType()), m_centOffset);
2217 83 : if (!correct) {
2218 0 : m_accidental->undoChangeProperty(Pid::ACCIDENTAL_TYPE, static_cast<int>(Accidental::centOffset2Subtype(m_centOffset)));
2219 : }
2220 : } else {
2221 0 : AccidentalType accType = Accidental::value2MicrotonalSubtype(tpc2alter(tpc()), quarterToneOffset());
2222 0 : updateLine();
2223 0 : EditNote::changeAccidental(score(), this, accType);
2224 : }
2225 : }
2226 :
2227 : // don't touch accidentals that don't concern tpc such as
2228 : // quarter tones
2229 56332 : if (!(m_accidental && Accidental::isMicrotonal(m_accidental->accidentalType()))) {
2230 : // calculate accidental
2231 56246 : AccidentalType acci = AccidentalType::NONE;
2232 :
2233 56246 : AccidentalVal accVal = tpc2alter(tpc());
2234 56246 : bool error = false;
2235 56246 : int eAbsLine = absStep(tpc(), epitch());
2236 56246 : AccidentalVal absLineAccVal = as->accidentalVal(eAbsLine, error);
2237 56246 : if (error) {
2238 0 : LOGD("error accidentalVal()");
2239 0 : return;
2240 : }
2241 56246 : if ((accVal != absLineAccVal) || hidden() || as->tieContext(eAbsLine) || as->forceRestateAccidental(eAbsLine)) {
2242 3755 : as->setAccidentalVal(eAbsLine, accVal, m_tieBack != 0 && m_accidental == 0);
2243 3755 : acci = Accidental::value2subtype(accVal);
2244 : // if previous tied note has same tpc, don't show accidental
2245 3755 : if (tieBackNonPartial() && m_tieBack->startNote()->tpc1() == tpc1()) {
2246 45 : acci = AccidentalType::NONE;
2247 3710 : } else if (acci == AccidentalType::NONE) {
2248 585 : acci = AccidentalType::NATURAL;
2249 : }
2250 52491 : } else if (hasAlteredUnison(this)) {
2251 3 : if ((acci = Accidental::value2subtype(accVal)) == AccidentalType::NONE) {
2252 3 : acci = AccidentalType::NATURAL;
2253 : }
2254 : }
2255 56246 : if (acci != AccidentalType::NONE && !m_hidden) {
2256 3713 : if (m_accidental == 0) {
2257 1908 : Accidental* a = Factory::createAccidental(this);
2258 1908 : a->setAccidentalType(acci);
2259 1908 : a->setVisible(visible());
2260 1908 : score()->undoAddElement(a);
2261 1805 : } else if (m_accidental->accidentalType() != acci) {
2262 100 : Accidental* a = m_accidental->clone();
2263 100 : a->setOwnershipParent(this);
2264 100 : a->setAccidentalType(acci);
2265 100 : score()->undoChangeElement(m_accidental, a);
2266 : }
2267 3713 : } else {
2268 52533 : if (m_accidental) {
2269 : // remove this if it was AUTO:
2270 1804 : if (m_accidental->role() == AccidentalRole::AUTO) {
2271 1621 : score()->undoRemoveElement(m_accidental);
2272 : } else {
2273 : // keep it, but update type if needed
2274 183 : acci = Accidental::value2subtype(accVal);
2275 183 : if (acci == AccidentalType::NONE) {
2276 142 : acci = AccidentalType::NATURAL;
2277 : }
2278 183 : if (m_accidental->accidentalType() != acci) {
2279 15 : Accidental* a = m_accidental->clone();
2280 15 : a->setOwnershipParent(this);
2281 15 : a->setAccidentalType(acci);
2282 15 : score()->undoChangeElement(m_accidental, a);
2283 : }
2284 : }
2285 : }
2286 : }
2287 : } else {
2288 : // microtonal accidentals playback as naturals
2289 : // in 1.X, they had no effect on accidental state of measure
2290 : // ultimately, they should probably get their own state
2291 : // for now, at least change state to natural, so subsequent notes playback as might be expected
2292 : // this is an incompatible change, but better to break it for 2.0 than wait until later
2293 86 : AccidentalVal accVal = Accidental::subtype2value(m_accidental->accidentalType());
2294 86 : as->setAccidentalVal(absLine, accVal, m_tieBack != 0 && m_accidental == 0);
2295 : }
2296 :
2297 56332 : as->setForceRestateAccidental(absLine, false);
2298 56332 : updateRelLine(absLine, true);
2299 : }
2300 :
2301 : //---------------------------------------------------------
2302 : // noteType
2303 : //---------------------------------------------------------
2304 :
2305 1458026 : NoteType Note::noteType() const
2306 : {
2307 1458026 : return chord()->noteType();
2308 : }
2309 :
2310 : //---------------------------------------------------------
2311 : // noteTypeUserName
2312 : //---------------------------------------------------------
2313 :
2314 0 : String Note::noteTypeUserName() const
2315 : {
2316 0 : switch (noteType()) {
2317 0 : case NoteType::ACCIACCATURA:
2318 0 : return muse::mtrc("engraving", "Acciaccatura");
2319 0 : case NoteType::APPOGGIATURA:
2320 0 : return muse::mtrc("engraving", "Appoggiatura");
2321 0 : case NoteType::GRACE8_AFTER:
2322 : case NoteType::GRACE16_AFTER:
2323 : case NoteType::GRACE32_AFTER:
2324 0 : return muse::mtrc("engraving", "Grace note after");
2325 0 : case NoteType::GRACE4:
2326 : case NoteType::GRACE16:
2327 : case NoteType::GRACE32:
2328 0 : return muse::mtrc("engraving", "Grace note before");
2329 0 : default:
2330 0 : return muse::mtrc("engraving", "Note");
2331 : }
2332 : }
2333 :
2334 : //---------------------------------------------------------
2335 : // scanElements
2336 : //---------------------------------------------------------
2337 :
2338 18716 : void Note::scanElements(std::function<void(EngravingItem*)> func)
2339 : {
2340 18716 : func(this);
2341 :
2342 18826 : for (EngravingItem* e : m_el) {
2343 110 : e->scanElements(func);
2344 : }
2345 18793 : for (Spanner* sp : m_spannerFor) {
2346 77 : sp->scanElements(func);
2347 : }
2348 :
2349 18716 : if (m_accidental) {
2350 791 : func(m_accidental);
2351 : }
2352 19009 : for (NoteDot* dot : m_dots) {
2353 293 : func(dot);
2354 : }
2355 18716 : }
2356 :
2357 : //---------------------------------------------------------
2358 : // setTrack
2359 : //---------------------------------------------------------
2360 :
2361 79899 : void Note::setTrack(track_idx_t val)
2362 : {
2363 79899 : EngravingItem::setTrack(val);
2364 79899 : if (tieForNonPartial()) {
2365 526 : m_tieFor->setTrack(val);
2366 1020 : for (SpannerSegment* seg : m_tieFor->spannerSegments()) {
2367 494 : seg->setTrack(val);
2368 : }
2369 : }
2370 79899 : if (tieBackNonPartial()) {
2371 220 : m_tieBack->setTrack2(val);
2372 : }
2373 79899 : if (laissezVib() || outgoingPartialTie()) {
2374 27 : m_tieFor->setTrack(val);
2375 27 : m_tieFor->setTrack2(val);
2376 31 : for (SpannerSegment* seg : m_tieFor->spannerSegments()) {
2377 4 : seg->setTrack(val);
2378 : }
2379 : }
2380 79899 : if (incomingPartialTie()) {
2381 31 : m_tieBack->setTrack(val);
2382 31 : m_tieBack->setTrack2(val);
2383 33 : for (SpannerSegment* seg : m_tieBack->spannerSegments()) {
2384 2 : seg->setTrack(val);
2385 : }
2386 : }
2387 80051 : for (Spanner* s : m_spannerFor) {
2388 152 : s->setTrack(val);
2389 : }
2390 80090 : for (Spanner* s : m_spannerBack) {
2391 191 : s->setTrack2(val);
2392 : }
2393 80129 : for (EngravingItem* e : m_el) {
2394 230 : e->setTrack(val);
2395 : }
2396 79899 : if (m_accidental) {
2397 4878 : m_accidental->setTrack(val);
2398 : }
2399 79899 : if (!chord()) { // if note is dragged with shift+ctrl
2400 30 : return;
2401 : }
2402 79952 : for (NoteDot* dot : m_dots) {
2403 83 : dot->setTrack(val);
2404 : }
2405 : }
2406 :
2407 : //---------------------------------------------------------
2408 : // reset
2409 : //---------------------------------------------------------
2410 :
2411 0 : void Note::reset()
2412 : {
2413 0 : undoChangeProperty(Pid::OFFSET, PointF());
2414 0 : undoResetProperty(Pid::LEADING_SPACE);
2415 0 : chord()->undoChangeProperty(Pid::OFFSET, PropertyValue::fromValue(PointF()));
2416 0 : chord()->undoChangeProperty(Pid::STEM_DIRECTION, PropertyValue::fromValue<DirectionV>(DirectionV::AUTO));
2417 0 : setOverrideBendVisibilityRules(false);
2418 0 : setHideGeneratedParens(false);
2419 0 : }
2420 :
2421 1558 : float Note::userVelocityFraction() const
2422 : {
2423 1558 : return m_userVelocity / 127.f;
2424 : }
2425 :
2426 : //---------------------------------------------------------
2427 : // mag
2428 : //---------------------------------------------------------
2429 :
2430 3620060 : double Note::mag() const
2431 : {
2432 3620060 : double m = chord() ? chord()->mag() : 1.0;
2433 3620060 : if (m_isSmall) {
2434 2861 : m *= style().styleD(Sid::smallNoteMag);
2435 : }
2436 3620060 : return m;
2437 : }
2438 :
2439 : //---------------------------------------------------------
2440 : // elementBase
2441 : //---------------------------------------------------------
2442 0 : EngravingItem* Note::elementBase() const
2443 : {
2444 0 : return chord();
2445 : }
2446 :
2447 : //---------------------------------------------------------
2448 : // setSmall
2449 : //---------------------------------------------------------
2450 :
2451 5327 : void Note::setSmall(bool val)
2452 : {
2453 5327 : m_isSmall = val;
2454 5327 : }
2455 :
2456 36589 : GuitarBend* Note::bendFor() const
2457 : {
2458 : /// Returns bend or dive going forward from this note. In the edge case of having both, returns the bend.
2459 36589 : GuitarBend* diveFor = nullptr;
2460 :
2461 39708 : for (Spanner* sp : m_spannerFor) {
2462 5325 : if (!sp->isGuitarBend()) {
2463 189 : continue;
2464 : }
2465 :
2466 5136 : GuitarBend* bend = toGuitarBend(sp);
2467 5136 : if (bend->bendType() == GuitarBendType::SCOOP) {
2468 6 : continue;
2469 : }
2470 :
2471 5130 : if (!bend->isDive()) {
2472 2206 : return bend;
2473 : } else {
2474 2924 : diveFor = bend;
2475 : }
2476 : }
2477 :
2478 34383 : return diveFor;
2479 : }
2480 :
2481 28065 : GuitarBend* Note::bendBack() const
2482 : {
2483 : /// Returns bend or dive going back from this note. In the edge case of having both, returns the bend.
2484 28065 : GuitarBend* diveBack = nullptr;
2485 :
2486 30272 : for (Spanner* sp : m_spannerBack) {
2487 4875 : if (!sp->isGuitarBend()) {
2488 181 : continue;
2489 : }
2490 :
2491 4694 : GuitarBend* bend = toGuitarBend(sp);
2492 4694 : if (bend->bendType() == GuitarBendType::SLIGHT_BEND || bend->bendType() == GuitarBendType::DIP) {
2493 223 : continue;
2494 : }
2495 :
2496 4471 : if (!bend->isDive()) {
2497 2668 : return bend;
2498 : } else {
2499 1803 : diveBack = bend;
2500 : }
2501 : }
2502 :
2503 25397 : return diveBack;
2504 : }
2505 :
2506 1176 : GuitarBend* Note::diveFor() const
2507 : {
2508 2306 : for (Spanner* sp : m_spannerFor) {
2509 1130 : if (sp->isGuitarBend() && toGuitarBend(sp)->isDive() && toGuitarBend(sp)->bendType() != GuitarBendType::SCOOP) {
2510 0 : return toGuitarBend(sp);
2511 : }
2512 : }
2513 :
2514 1176 : return nullptr;
2515 : }
2516 :
2517 6167 : GuitarBend* Note::diveBack() const
2518 : {
2519 7890 : for (Spanner* sp : m_spannerBack) {
2520 2844 : if (sp->isGuitarBend() && toGuitarBend(sp)->isDive() && toGuitarBend(sp)->bendType() != GuitarBendType::DIP) {
2521 1121 : return toGuitarBend(sp);
2522 : }
2523 : }
2524 :
2525 5046 : return nullptr;
2526 : }
2527 :
2528 86229 : Tie* Note::tieForNonPartial() const
2529 : {
2530 86229 : if (!m_tieFor || m_tieFor->type() != ElementType::TIE) {
2531 85515 : return nullptr;
2532 : }
2533 :
2534 714 : return m_tieFor;
2535 : }
2536 :
2537 166710 : Tie* Note::tieBackNonPartial() const
2538 : {
2539 166710 : if (!m_tieBack || m_tieBack->type() != ElementType::TIE) {
2540 163613 : return nullptr;
2541 : }
2542 :
2543 3097 : return m_tieBack;
2544 : }
2545 :
2546 566578 : LaissezVib* Note::laissezVib() const
2547 : {
2548 566578 : if (!m_tieFor || !m_tieFor->isLaissezVib()) {
2549 566253 : return nullptr;
2550 : }
2551 :
2552 325 : return toLaissezVib(m_tieFor);
2553 : }
2554 :
2555 144751 : PartialTie* Note::incomingPartialTie() const
2556 : {
2557 144751 : if (!m_tieBack || !m_tieBack->isPartialTie()) {
2558 144335 : return nullptr;
2559 : }
2560 :
2561 416 : return toPartialTie(m_tieBack);
2562 : }
2563 :
2564 120343 : PartialTie* Note::outgoingPartialTie() const
2565 : {
2566 120343 : if (!m_tieFor || !m_tieFor->isPartialTie()) {
2567 120272 : return nullptr;
2568 : }
2569 :
2570 71 : return toPartialTie(m_tieFor);
2571 : }
2572 :
2573 3519 : void Note::setTieFor(Tie* t)
2574 : {
2575 3519 : if (!t) {
2576 1365 : m_jumpPoints.clear();
2577 : }
2578 3519 : m_tieFor = t;
2579 3519 : if (m_tieFor && !m_tieFor->isLaissezVib()) {
2580 2133 : m_tieFor->updatePossibleJumpPoints();
2581 : }
2582 3519 : }
2583 :
2584 3201 : void Note::setTieBack(Tie* t)
2585 : {
2586 3201 : if (m_tieBack && t && m_tieBack->jumpPoint()) {
2587 0 : t->setJumpPoint(m_tieBack->jumpPoint());
2588 0 : m_tieBack->setJumpPoint(nullptr);
2589 : }
2590 3201 : m_tieBack = t;
2591 3201 : }
2592 :
2593 : //---------------------------------------------------------
2594 : // line
2595 : //---------------------------------------------------------
2596 :
2597 1250292 : int Note::line() const
2598 : {
2599 1250292 : return fixed() ? m_fixedLine : m_line;
2600 : }
2601 :
2602 : //---------------------------------------------------------
2603 : // setHeadScheme
2604 : //---------------------------------------------------------
2605 :
2606 70 : void Note::setHeadScheme(NoteHeadScheme val)
2607 : {
2608 70 : IF_ASSERT_FAILED(int(val) >= -1 && int(val) < int(NoteHeadScheme::HEAD_SCHEMES)) {
2609 0 : val = NoteHeadScheme::HEAD_AUTO;
2610 : }
2611 70 : m_headScheme = val;
2612 70 : }
2613 :
2614 : //---------------------------------------------------------
2615 : // setHeadGroup
2616 : //---------------------------------------------------------
2617 :
2618 8108 : void Note::setHeadGroup(NoteHeadGroup val)
2619 : {
2620 8108 : assert(int(val) >= 0 && int(val) < int(NoteHeadGroup::HEAD_GROUPS));
2621 8108 : m_headGroup = val;
2622 8108 : }
2623 :
2624 : //---------------------------------------------------------
2625 : // ottaveCapoFret
2626 : // offset added by Ottava's and Capo Fret.
2627 : //---------------------------------------------------------
2628 :
2629 13002 : int Note::ottaveCapoFret() const
2630 : {
2631 13002 : const Chord* ch = chord();
2632 13002 : Fraction segmentTick = ch->segment()->tick();
2633 13002 : const Staff* staff = this->staff();
2634 :
2635 13002 : const CapoParams& capo = staff->capo(segmentTick);
2636 13002 : int capoFret = 0;
2637 :
2638 : using MODE = CapoParams::TransposeMode;
2639 13002 : if (capo.active && MODE::PLAYBACK_ONLY == capo.transposeMode) {
2640 0 : if (capo.ignoredStrings.empty() || !muse::contains(capo.ignoredStrings, static_cast<string_idx_t>(m_string))) {
2641 0 : capoFret = capo.fretPosition;
2642 : }
2643 : }
2644 :
2645 26004 : return staff->pitchOffset(segmentTick) + capoFret;
2646 : }
2647 :
2648 : //---------------------------------------------------------
2649 : // ppitch
2650 : // playback pitch
2651 : //---------------------------------------------------------
2652 :
2653 10186 : int Note::ppitch() const
2654 : {
2655 10186 : const Chord* ch = chord();
2656 :
2657 : // if staff is drum
2658 : // match tremolo and articulation between variants and chord
2659 10186 : if (m_play && ch) {
2660 10127 : const Staff* staff = ch->staff();
2661 10127 : Fraction tick = ch->tick();
2662 :
2663 10127 : if (staff && staff->isDrumStaff(tick)) {
2664 105 : const Drumset* ds = originPart()->instrument(tick)->drumset();
2665 105 : if (ds) {
2666 105 : DrumInstrumentVariant div = ds->findVariant(m_pitch, ch->articulations(), ch->tremoloType());
2667 105 : if (div.pitch != INVALID_PITCH) {
2668 0 : return div.pitch;
2669 : }
2670 105 : }
2671 : }
2672 : }
2673 :
2674 10186 : return m_pitch + ottaveCapoFret() + linkedOttavaPitchOffset();
2675 : }
2676 :
2677 : //---------------------------------------------------------
2678 : // epitch
2679 : // effective pitch, i.e. a pitch which is visible in the
2680 : // currently used written notation.
2681 : // honours transposing instruments
2682 : //---------------------------------------------------------
2683 :
2684 211529 : int Note::epitch() const
2685 : {
2686 211529 : return m_pitch - (concertPitch() ? 0 : transposition()) + linkedOttavaPitchOffset();
2687 : }
2688 :
2689 : //---------------------------------------------------------
2690 : // linkedOttavaPitchOffset
2691 : // if an ottava exists in another part but has been excluded
2692 : // from this one, this returns the corresponding pitch offset
2693 : //---------------------------------------------------------
2694 :
2695 221715 : int Note::linkedOttavaPitchOffset() const
2696 : {
2697 221715 : int linkedOttavaPitchOffset = 0;
2698 221715 : if (links() && staff()->pitchOffset(tick()) == 0) {
2699 36095 : for (EngravingObject* link : *links()) {
2700 24188 : int offset = toNote(link)->staff()->pitchOffset(tick());
2701 24188 : if (offset != 0) {
2702 0 : linkedOttavaPitchOffset = offset;
2703 0 : break;
2704 : }
2705 : }
2706 : }
2707 221715 : return linkedOttavaPitchOffset;
2708 : }
2709 :
2710 : //---------------------------------------------------------
2711 : // octave
2712 : // compute octave number from note information
2713 : //---------------------------------------------------------
2714 :
2715 1242 : int Note::octave() const
2716 : {
2717 1242 : return ((epitch() + ottaveCapoFret() - static_cast<int>(tpc2alter(tpc()))) / 12) - 1;
2718 : }
2719 :
2720 1574 : int Note::playingOctave() const
2721 : {
2722 1574 : return mu::engraving::playingOctave(ppitch(), tpc1());
2723 : }
2724 :
2725 1558 : double Note::playingTuning() const
2726 : {
2727 1558 : return m_tuning + m_centOffset;
2728 : }
2729 :
2730 : //---------------------------------------------------------
2731 : // customizeVelocity
2732 : // Input is the global velocity determined by dynamic
2733 : // signs and crescendo/diminuendo etc.
2734 : // Returns the actual play velocity for this note
2735 : // modified by veloOffset
2736 : //---------------------------------------------------------
2737 :
2738 0 : int Note::customizeVelocity(int velo) const
2739 : {
2740 0 : if (m_veloType == VeloType::OFFSET_VAL) {
2741 0 : velo = velo + (velo * userVelocity()) / 100;
2742 0 : } else if (m_veloType == VeloType::USER_VAL) {
2743 0 : velo = userVelocity();
2744 : }
2745 0 : return std::clamp(velo, 1, 127);
2746 : }
2747 :
2748 : //---------------------------------------------------------
2749 : // startDrag
2750 : //---------------------------------------------------------
2751 :
2752 0 : void Note::startDrag(EditData& ed)
2753 : {
2754 0 : std::shared_ptr<NoteEditData> ned = std::make_shared<NoteEditData>();
2755 0 : ned->e = this;
2756 0 : ned->line = m_line;
2757 0 : ned->string = m_string;
2758 0 : ned->pushProperty(Pid::PITCH);
2759 0 : ned->pushProperty(Pid::TPC1);
2760 0 : ned->pushProperty(Pid::TPC2);
2761 0 : ned->pushProperty(Pid::FRET);
2762 0 : ned->pushProperty(Pid::STRING);
2763 :
2764 0 : ed.addData(ned);
2765 0 : }
2766 :
2767 : //---------------------------------------------------------
2768 : // drag
2769 : //---------------------------------------------------------
2770 :
2771 0 : RectF Note::drag(EditData& ed)
2772 : {
2773 0 : NoteEditData* noteEditData = static_cast<NoteEditData*>(ed.getData(this).get());
2774 0 : IF_ASSERT_FAILED(noteEditData) {
2775 0 : return RectF();
2776 : }
2777 :
2778 0 : if (ed.isEditMode) {
2779 0 : dragInEditMode(ed);
2780 0 : return RectF();
2781 : }
2782 :
2783 0 : PointF delta = ed.evtDelta;
2784 0 : noteEditData->delta = delta;
2785 :
2786 0 : if (noteEditData->mode == NoteEditData::EditMode_Undefined) {
2787 0 : noteEditData->mode = NoteEditData::editModeByDragDirection(delta.x(), delta.y());
2788 : }
2789 :
2790 0 : bool isSingleNoteSelection = score()->getSelectedElement() == this;
2791 0 : if (noteEditData->mode == NoteEditData::EditMode_AddSpacing && isSingleNoteSelection && !(ed.modifiers & ControlModifier)) {
2792 0 : horizontalDrag(ed);
2793 0 : } else if (noteEditData->mode == NoteEditData::EditMode_ChangePitch) {
2794 0 : verticalDrag(ed);
2795 : }
2796 :
2797 0 : return RectF();
2798 : }
2799 :
2800 : //---------------------------------------------------------
2801 : // endDrag
2802 : //---------------------------------------------------------
2803 :
2804 0 : void Note::endDrag(EditData& ed)
2805 : {
2806 0 : NoteEditData* ned = static_cast<NoteEditData*>(ed.getData(this).get());
2807 0 : IF_ASSERT_FAILED(ned) {
2808 0 : return;
2809 : }
2810 0 : for (Note* nn : tiedNotes()) {
2811 0 : for (const auto& [id, data, flags] : ned->propertyData) {
2812 0 : setPropertyFlags(id, flags); // reset initial property flags state
2813 0 : score()->undoPropertyChanged(nn, id, data);
2814 : }
2815 0 : }
2816 : }
2817 :
2818 : //---------------------------------------------------------
2819 : // dragInEditMode
2820 : //---------------------------------------------------------
2821 :
2822 0 : void Note::dragInEditMode(EditData& editData)
2823 : {
2824 0 : Chord* ch = chord();
2825 0 : Segment* seg = ch->segment();
2826 :
2827 0 : if (editData.modifiers & ShiftModifier) {
2828 0 : const Spatium deltaSp = Spatium(editData.delta.x() / spatium());
2829 0 : seg->undoChangeProperty(Pid::LEADING_SPACE, seg->extraLeadingSpace() + deltaSp);
2830 0 : } else if (ch->notes().size() == 1) {
2831 : // if the chord contains only this note, then move the whole chord
2832 : // including stem, flag etc.
2833 0 : ch->undoChangeProperty(Pid::OFFSET, PropertyValue::fromValue(ch->offset() + offset() + editData.evtDelta));
2834 0 : setOffset(PointF());
2835 : } else {
2836 0 : setOffset(offset() + editData.evtDelta);
2837 : }
2838 :
2839 0 : triggerLayout();
2840 0 : }
2841 :
2842 : //---------------------------------------------------------
2843 : // verticalDrag
2844 : //---------------------------------------------------------
2845 :
2846 0 : void Note::verticalDrag(EditData& ed)
2847 : {
2848 0 : Fraction _tick = chord()->tick();
2849 0 : if (originPart()->instrument(_tick)->useDrumset()) {
2850 0 : return;
2851 : }
2852 :
2853 0 : NoteEditData* ned = static_cast<NoteEditData*>(ed.getData(this).get());
2854 :
2855 0 : const bool tab = staffType()->isTabStaff();
2856 0 : double step = spatium() * staffType()->lineDistance().val() * (tab ? 1.0 : 0.5);
2857 0 : int lineOffset = lrint(ed.moveDelta.y() / step);
2858 :
2859 0 : if (tab) {
2860 0 : const StringData* strData = originPart()->stringData(_tick, staffIdx());
2861 0 : const int pitchOffset = staff()->pitchOffset(_tick);
2862 0 : int nString = ned->string + (staffType()->upsideDown() ? -lineOffset : lineOffset);
2863 0 : int nFret = strData->fret(m_pitch + pitchOffset, nString, staff(), tick());
2864 :
2865 0 : if (nFret >= 0) { // no fret?
2866 0 : if (fret() != nFret || string() != nString) {
2867 0 : for (Note* nn : tiedNotes()) {
2868 0 : nn->setFret(nFret);
2869 0 : nn->setString(nString);
2870 0 : strData->fretChords(nn->chord());
2871 0 : nn->triggerLayout();
2872 0 : }
2873 : }
2874 : }
2875 : } else {
2876 0 : staff_idx_t idx = chord()->vStaffIdx();
2877 0 : bool error = false;
2878 0 : AccidentalVal accOffs = firstTiedNote()->chord()->measure()->findAccidental(
2879 0 : firstTiedNote()->chord()->segment(), idx, ned->line + lineOffset, error);
2880 0 : if (error) {
2881 0 : accOffs = Accidental::subtype2value(AccidentalType::NONE);
2882 : }
2883 0 : int nStep = absStep(ned->line + lineOffset, score()->staff(idx)->clef(_tick));
2884 0 : nStep = std::max(0, nStep);
2885 0 : int octave = nStep / STEP_DELTA_OCTAVE;
2886 0 : int newPitch = step2pitch(nStep) + octave * PITCH_DELTA_OCTAVE + int(accOffs);
2887 :
2888 0 : int newTpc1 = step2tpc(nStep % STEP_DELTA_OCTAVE, accOffs);
2889 0 : int newTpc2 = newTpc1;
2890 0 : if (concertPitch()) {
2891 0 : newTpc2 = transposeTpc(newTpc1);
2892 : } else {
2893 0 : newPitch += staff()->transpose(_tick).chromatic;
2894 0 : newTpc1 = transposeTpc(newTpc2);
2895 : }
2896 :
2897 0 : int clampedPitch = clampPitch(newPitch);
2898 0 : if (clampedPitch != newPitch) {
2899 : // newTpc1/newTpc2 were derived from a step/accidental that no longer
2900 : // matches the clamped pitch; respell them so tpc and pitch stay in sync
2901 0 : Key key = score()->staff(idx)->key(_tick);
2902 0 : if (concertPitch()) {
2903 0 : newTpc1 = pitch2tpc(clampedPitch, key, Prefer::NEAREST);
2904 0 : newTpc2 = transposeTpc(newTpc1);
2905 : } else {
2906 0 : newTpc2 = pitch2tpc(clampedPitch - staff()->transpose(_tick).chromatic, key, Prefer::NEAREST);
2907 0 : newTpc1 = transposeTpc(newTpc2);
2908 : }
2909 0 : newPitch = clampedPitch;
2910 : }
2911 :
2912 0 : for (Note* nn : tiedNotes()) {
2913 0 : nn->setAccidentalType(AccidentalType::NONE);
2914 0 : nn->setPitch(newPitch, newTpc1, newTpc2);
2915 0 : nn->triggerLayout();
2916 0 : }
2917 : }
2918 0 : score()->inputState().setAccidentalType(AccidentalType::NONE);
2919 : }
2920 :
2921 : //---------------------------------------------------------
2922 : // normalizeLeftDragDelta
2923 : //---------------------------------------------------------
2924 :
2925 0 : void Note::normalizeLeftDragDelta(Segment* seg, EditData& ed, NoteEditData* ned)
2926 : {
2927 0 : Segment* previous = seg->prev();
2928 :
2929 0 : if (previous) {
2930 0 : double minDist = HorizontalSpacing::minHorizontalDistance(previous, seg, 1.0);
2931 :
2932 0 : double diff = (ed.pos.x()) - (previous->pageX() + minDist);
2933 :
2934 0 : double distanceBetweenSegments = (previous->pageX() + minDist) - seg->pageX();
2935 :
2936 0 : if (diff < 0) {
2937 0 : ned->delta.setX(distanceBetweenSegments);
2938 : }
2939 : } else {
2940 0 : Measure* measure = seg->measure();
2941 :
2942 0 : double minDist = style().styleAbsolute(Sid::barNoteDistance);
2943 :
2944 0 : double diff = (ed.pos.x()) - (measure->pageX() + minDist);
2945 :
2946 0 : double distanceBetweenSegments = (measure->pageX() + minDist) - seg->pageX();
2947 :
2948 0 : if (diff < 0) {
2949 0 : ned->delta.setX(distanceBetweenSegments);
2950 : }
2951 : }
2952 0 : }
2953 :
2954 : //---------------------------------------------------------
2955 : // horizontalDrag
2956 : //---------------------------------------------------------
2957 :
2958 0 : void Note::horizontalDrag(EditData& ed)
2959 : {
2960 0 : Chord* ch = chord();
2961 0 : Segment* seg = ch->segment();
2962 :
2963 0 : NoteEditData* ned = static_cast<NoteEditData*>(ed.getData(this).get());
2964 :
2965 0 : if (ed.moveDelta.x() < 0) {
2966 0 : normalizeLeftDragDelta(seg, ed, ned);
2967 : }
2968 :
2969 0 : const Spatium deltaSp = Spatium(ned->delta.x() / spatium());
2970 :
2971 0 : if (seg->extraLeadingSpace() + deltaSp < 0_sp) {
2972 0 : return;
2973 : }
2974 :
2975 0 : seg->undoChangeProperty(Pid::LEADING_SPACE, seg->extraLeadingSpace() + deltaSp);
2976 :
2977 0 : triggerLayout();
2978 : }
2979 :
2980 : //---------------------------------------------------------
2981 : // updateRelLine
2982 : // calculate the real note line depending on clef,
2983 : // absLine is the absolute line
2984 : //---------------------------------------------------------
2985 :
2986 56827 : void Note::updateRelLine(int absLine, bool undoable)
2987 : {
2988 56827 : if (!staff()) {
2989 0 : return;
2990 : }
2991 : // int idx = staffIdx() + chord()->staffMove();
2992 56827 : assert(staffIdx() == chord()->staffIdx());
2993 56827 : staff_idx_t idx = chord()->vStaffIdx();
2994 :
2995 56827 : const Staff* staff = score()->staff(idx);
2996 56827 : const StaffType* st = staff->staffTypeForElement(this);
2997 56827 : if (st->isTabStaff()) {
2998 : // tab staff is already correct, and the following relStep method doesn't apply whatsoever to tab staves
2999 1 : return;
3000 : }
3001 56826 : ClefType clef = staff->clef(chord()->tick());
3002 56826 : int line = relStep(absLine, clef);
3003 :
3004 56826 : if (undoable && (m_line != INVALID_LINE) && (line != m_line)) {
3005 726 : undoChangeProperty(Pid::LINE, line);
3006 : } else {
3007 56100 : setLine(line);
3008 : }
3009 :
3010 56826 : int off = st->stepOffset();
3011 56826 : double ld = st->lineDistance().val();
3012 56826 : mutldata()->setPosY((m_line + off * 2.0) * spatium() * .5 * ld);
3013 : }
3014 :
3015 : //---------------------------------------------------------
3016 : // updateLine
3017 : //---------------------------------------------------------
3018 :
3019 495 : void Note::updateLine()
3020 : {
3021 495 : int absLine = absStep(tpc(), epitch());
3022 495 : updateRelLine(absLine, false);
3023 495 : }
3024 :
3025 : //---------------------------------------------------------
3026 : // setNval
3027 : // set note properties from NoteVal
3028 : //---------------------------------------------------------
3029 :
3030 5227 : void Note::setNval(const NoteVal& nval, Fraction tick)
3031 : {
3032 5227 : setPitch(nval.pitch);
3033 5227 : m_userVelocity = nval.velocityOverride;
3034 5227 : m_fret = nval.fret;
3035 5227 : m_string = nval.string;
3036 :
3037 5227 : m_tpc[0] = nval.tpc1;
3038 5227 : m_tpc[1] = nval.tpc2;
3039 :
3040 5227 : if (tick == Fraction(-1, 1) && chord()) {
3041 1657 : tick = chord()->tick();
3042 : }
3043 5227 : Interval v = staff()->transpose(tick);
3044 5227 : if (nval.tpc1 == Tpc::TPC_INVALID) {
3045 3327 : if (nval.tpc2 == Tpc::TPC_INVALID) {
3046 1142 : Key key = staff()->concertKey(tick);
3047 1142 : m_tpc[0] = pitch2tpc(nval.pitch, key, Prefer::NEAREST);
3048 : } else {
3049 2185 : if (v.isZero()) {
3050 2060 : m_tpc[0] = m_tpc[1];
3051 : } else {
3052 125 : m_tpc[0] = Transpose::transposeTpc(m_tpc[1], v, true);
3053 : }
3054 : }
3055 : }
3056 5227 : if (nval.tpc2 == Tpc::TPC_INVALID) {
3057 1286 : if (v.isZero()) {
3058 1142 : m_tpc[1] = m_tpc[0];
3059 : } else {
3060 144 : v.flip();
3061 144 : m_tpc[1] = Transpose::transposeTpc(m_tpc[0], v, true);
3062 : }
3063 : }
3064 :
3065 5227 : m_headGroup = NoteHeadGroup(nval.headGroup);
3066 5227 : }
3067 :
3068 : //---------------------------------------------------------
3069 : // localSpatiumChanged
3070 : //---------------------------------------------------------
3071 :
3072 0 : void Note::localSpatiumChanged(double oldValue, double newValue)
3073 : {
3074 0 : EngravingItem::localSpatiumChanged(oldValue, newValue);
3075 0 : for (EngravingItem* e : dots()) {
3076 0 : e->localSpatiumChanged(oldValue, newValue);
3077 : }
3078 0 : for (EngravingItem* e : el()) {
3079 0 : e->localSpatiumChanged(oldValue, newValue);
3080 : }
3081 0 : for (Spanner* spanner : spannerBack()) {
3082 0 : for (auto k : spanner->spannerSegments()) {
3083 0 : k->localSpatiumChanged(oldValue, newValue);
3084 : }
3085 : }
3086 0 : }
3087 :
3088 : //---------------------------------------------------------
3089 : // getProperty
3090 : //---------------------------------------------------------
3091 :
3092 721938 : PropertyValue Note::getProperty(Pid propertyId) const
3093 : {
3094 721938 : switch (propertyId) {
3095 23580 : case Pid::PITCH:
3096 23580 : return pitch();
3097 23580 : case Pid::CENT_OFFSET:
3098 23580 : return centOffset();
3099 47355 : case Pid::TPC1:
3100 47355 : return m_tpc[0];
3101 24020 : case Pid::TPC2:
3102 24020 : return m_tpc[1];
3103 23580 : case Pid::SMALL:
3104 23580 : return isSmall();
3105 23580 : case Pid::MIRROR_HEAD:
3106 23580 : return userMirror();
3107 23580 : case Pid::DOT_POSITION:
3108 23580 : return PropertyValue::fromValue<DirectionV>(userDotPosition());
3109 23580 : case Pid::HEAD_SCHEME:
3110 23580 : return headScheme();
3111 26235 : case Pid::HEAD_GROUP:
3112 26235 : return headGroup();
3113 23580 : case Pid::USER_VELOCITY:
3114 23580 : return userVelocity();
3115 23580 : case Pid::TUNING:
3116 23580 : return tuning();
3117 24141 : case Pid::FRET:
3118 24141 : return fret();
3119 24134 : case Pid::STRING:
3120 24134 : return string();
3121 23580 : case Pid::GHOST:
3122 23580 : return ghost();
3123 23580 : case Pid::DEAD:
3124 23580 : return deadNote();
3125 23580 : case Pid::HEAD_TYPE:
3126 23580 : return headType();
3127 0 : case Pid::VELO_TYPE:
3128 0 : return m_veloType;
3129 23993 : case Pid::PLAY:
3130 23993 : return play();
3131 2325 : case Pid::LINE:
3132 2325 : return m_line;
3133 23924 : case Pid::FIXED:
3134 23924 : return fixed();
3135 23924 : case Pid::FIXED_LINE:
3136 23924 : return fixedLine();
3137 27623 : case Pid::HAS_PARENTHESES:
3138 27623 : return m_hasParens ? ParenthesesMode::BOTH : ParenthesesMode::NONE;
3139 23701 : case Pid::HIDE_GENERATED_PARENTHESES:
3140 23701 : return m_hideGeneratedParens;
3141 47192 : case Pid::POSITION_LINKED_TO_MASTER:
3142 : case Pid::APPEARANCE_LINKED_TO_MASTER:
3143 47192 : if (chord()) {
3144 47192 : return EngravingItem::getProperty(propertyId).toBool() && chord()->getProperty(propertyId).toBool();
3145 : }
3146 : default:
3147 143991 : break;
3148 : }
3149 143991 : return EngravingItem::getProperty(propertyId);
3150 : }
3151 :
3152 : //---------------------------------------------------------
3153 : // setProperty
3154 : //---------------------------------------------------------
3155 :
3156 3575 : bool Note::setProperty(Pid propertyId, const PropertyValue& v)
3157 : {
3158 3575 : Measure* m = chord() ? chord()->measure() : nullptr;
3159 3575 : switch (propertyId) {
3160 1 : case Pid::PITCH:
3161 1 : setPitch(v.toInt());
3162 1 : break;
3163 0 : case Pid::CENT_OFFSET:
3164 0 : setCentOffset(v.toDouble());
3165 0 : break;
3166 67 : case Pid::TPC1:
3167 67 : m_tpc[0] = v.toInt();
3168 67 : break;
3169 143 : case Pid::TPC2:
3170 143 : m_tpc[1] = v.toInt();
3171 143 : break;
3172 873 : case Pid::LINE:
3173 873 : setLine(v.toInt());
3174 873 : break;
3175 2 : case Pid::SMALL:
3176 2 : setSmall(v.toBool());
3177 2 : break;
3178 7 : case Pid::MIRROR_HEAD:
3179 7 : setUserMirror(v.value<DirectionH>());
3180 7 : break;
3181 7 : case Pid::DOT_POSITION:
3182 7 : setUserDotPosition(v.value<DirectionV>());
3183 7 : break;
3184 6 : case Pid::HEAD_SCHEME:
3185 6 : setHeadScheme(v.value<NoteHeadScheme>());
3186 6 : break;
3187 753 : case Pid::HEAD_GROUP:
3188 753 : setHeadGroup(v.value<NoteHeadGroup>());
3189 753 : if (links()) {
3190 0 : for (EngravingObject* scoreElement : *links()) {
3191 0 : Note* note = toNote(scoreElement);
3192 0 : note->setDeadNote(staff() && !staff()->isDrumStaff(tick()) && m_headGroup == NoteHeadGroup::HEAD_CROSS);
3193 0 : note->setHeadGroup(m_headGroup);
3194 : }
3195 : } else {
3196 753 : setDeadNote(staff() && !staff()->isDrumStaff(tick()) && m_headGroup == NoteHeadGroup::HEAD_CROSS);
3197 753 : setHeadGroup(m_headGroup);
3198 : }
3199 753 : break;
3200 1 : case Pid::USER_VELOCITY:
3201 1 : setUserVelocity(v.toInt());
3202 1 : break;
3203 1 : case Pid::TUNING:
3204 1 : setTuning(v.toDouble());
3205 1 : break;
3206 167 : case Pid::FRET:
3207 167 : setFret(v.toInt());
3208 167 : break;
3209 158 : case Pid::STRING:
3210 158 : setString(v.toInt());
3211 158 : break;
3212 2 : case Pid::GHOST:
3213 2 : setGhost(v.toBool());
3214 2 : break;
3215 0 : case Pid::DEAD:
3216 0 : setDeadNote(v.toBool());
3217 0 : if (!staff()->isDrumStaff(tick())) {
3218 0 : NoteHeadGroup head
3219 0 : = (m_deadNote && m_headGroup != NoteHeadGroup::HEAD_CROSS) ? NoteHeadGroup::HEAD_CROSS : NoteHeadGroup::HEAD_NORMAL;
3220 0 : setHeadGroup(head);
3221 : }
3222 0 : break;
3223 118 : case Pid::HEAD_TYPE:
3224 118 : setHeadType(v.value<NoteHeadType>());
3225 118 : break;
3226 3 : case Pid::VELO_TYPE:
3227 3 : m_veloType = v.value<VeloType>();
3228 3 : break;
3229 79 : case Pid::VISIBLE: {
3230 79 : setVisible(v.toBool());
3231 79 : if (m) {
3232 79 : m->checkMultiVoices(chord()->staffIdx());
3233 : }
3234 79 : break;
3235 : }
3236 164 : case Pid::PLAY:
3237 164 : setPlay(v.toBool());
3238 164 : break;
3239 164 : case Pid::FIXED:
3240 164 : setFixed(v.toBool());
3241 164 : break;
3242 164 : case Pid::FIXED_LINE:
3243 164 : setFixedLine(v.toInt());
3244 164 : break;
3245 165 : case Pid::HAS_PARENTHESES:
3246 165 : if (v.value<ParenthesesMode>() != ParenthesesMode::BOTH && v.value<ParenthesesMode>() != ParenthesesMode::NONE) {
3247 0 : ASSERT_X("Notes cannot set left & right parens individually");
3248 : }
3249 165 : m_hasParens = v.value<ParenthesesMode>() == ParenthesesMode::BOTH;
3250 165 : break;
3251 53 : case Pid::HIDE_GENERATED_PARENTHESES:
3252 53 : setHideGeneratedParens(v.toBool());
3253 53 : break;
3254 32 : case Pid::POSITION_LINKED_TO_MASTER:
3255 : case Pid::APPEARANCE_LINKED_TO_MASTER:
3256 32 : if (v.toBool() == true && chord()) {
3257 : // when re-linking, also re-link the parent chord
3258 0 : chord()->setProperty(propertyId, v);
3259 : }
3260 : // fall through
3261 : default:
3262 477 : if (!EngravingItem::setProperty(propertyId, v)) {
3263 0 : return false;
3264 : }
3265 477 : break;
3266 : }
3267 3575 : triggerLayout();
3268 3575 : return true;
3269 : }
3270 :
3271 : //---------------------------------------------------------
3272 : // propertyDefault
3273 : //---------------------------------------------------------
3274 :
3275 683940 : PropertyValue Note::propertyDefault(Pid propertyId) const
3276 : {
3277 683940 : switch (propertyId) {
3278 23580 : case Pid::CENT_OFFSET:
3279 23580 : return 0.0;
3280 70740 : case Pid::GHOST:
3281 : case Pid::DEAD:
3282 : case Pid::SMALL:
3283 70740 : return false;
3284 23580 : case Pid::MIRROR_HEAD:
3285 23580 : return DirectionH::AUTO;
3286 23580 : case Pid::DOT_POSITION:
3287 23580 : return DirectionV::AUTO;
3288 23580 : case Pid::HEAD_SCHEME:
3289 23580 : return NoteHeadScheme::HEAD_AUTO;
3290 23580 : case Pid::HEAD_GROUP:
3291 23580 : return NoteHeadGroup::HEAD_NORMAL;
3292 23580 : case Pid::USER_VELOCITY:
3293 23580 : return 0;
3294 23580 : case Pid::TUNING:
3295 23580 : return 0.0;
3296 47160 : case Pid::FRET:
3297 : case Pid::STRING:
3298 47160 : return -1;
3299 23580 : case Pid::HEAD_TYPE:
3300 23580 : return NoteHeadType::HEAD_AUTO;
3301 0 : case Pid::VELO_TYPE:
3302 0 : return VeloType::USER_VAL;
3303 23580 : case Pid::PLAY:
3304 23580 : return true;
3305 23580 : case Pid::FIXED:
3306 23580 : return false;
3307 23580 : case Pid::FIXED_LINE:
3308 23580 : return 0;
3309 23580 : case Pid::TPC2:
3310 23580 : return getProperty(Pid::TPC1);
3311 47160 : case Pid::PITCH:
3312 : case Pid::TPC1:
3313 47160 : return PropertyValue();
3314 23580 : case Pid::VISIBLE:
3315 23580 : if (staffType() && staffType()->isTabStaff()) {
3316 401 : GuitarBend* bend = bendBack();
3317 401 : if (bend && !bend->isFullRelease()) {
3318 0 : return false;
3319 : }
3320 : }
3321 23580 : return EngravingItem::propertyDefault(propertyId);
3322 23629 : case Pid::COLOR: {
3323 23629 : const StaffType* st = staffType();
3324 23629 : if (st && st->isTabStaff() && st->fretUseTextStyle()) {
3325 448 : return style().styleV(Sid::tabFretNumberColor);
3326 : }
3327 23181 : return EngravingItem::propertyDefault(propertyId);
3328 : }
3329 23580 : case Pid::HIDE_GENERATED_PARENTHESES:
3330 23580 : return false;
3331 165131 : default:
3332 165131 : break;
3333 : }
3334 165131 : return EngravingItem::propertyDefault(propertyId);
3335 : }
3336 :
3337 562 : void Note::styleChanged()
3338 : {
3339 562 : const StaffType* st = staffType();
3340 562 : if (st && st->isTabStaff() && st->fretUseTextStyle()) {
3341 0 : setProperty(Pid::COLOR, style().styleV(Sid::tabFretNumberColor));
3342 : }
3343 562 : EngravingItem::styleChanged();
3344 562 : }
3345 :
3346 : //---------------------------------------------------------
3347 : // setHeadType
3348 : //---------------------------------------------------------
3349 :
3350 214 : void Note::setHeadType(NoteHeadType t)
3351 : {
3352 214 : m_headType = t;
3353 214 : }
3354 :
3355 : //---------------------------------------------------------
3356 : // setOnTimeOffset
3357 : //---------------------------------------------------------
3358 :
3359 0 : void Note::setOnTimeOffset(int val)
3360 : {
3361 0 : m_playEvents[0].setOntime(val);
3362 0 : chord()->setPlayEventType(PlayEventType::User);
3363 0 : }
3364 :
3365 : //---------------------------------------------------------
3366 : // setOffTimeOffset
3367 : //---------------------------------------------------------
3368 :
3369 0 : void Note::setOffTimeOffset(int val)
3370 : {
3371 0 : m_playEvents[0].setLen(val - m_playEvents[0].ontime());
3372 0 : chord()->setPlayEventType(PlayEventType::User);
3373 0 : }
3374 :
3375 : //---------------------------------------------------------
3376 : // setScore
3377 : //---------------------------------------------------------
3378 :
3379 5574 : void Note::setScore(Score* s)
3380 : {
3381 5574 : EngravingItem::setScore(s);
3382 5574 : if (m_tieFor) {
3383 83 : m_tieFor->setScore(s);
3384 : }
3385 5574 : if (m_accidental) {
3386 32 : m_accidental->setScore(s);
3387 : }
3388 5603 : for (NoteDot* dot : m_dots) {
3389 29 : dot->setScore(s);
3390 : }
3391 5599 : for (EngravingItem* el : m_el) {
3392 25 : el->setScore(s);
3393 : }
3394 5574 : }
3395 :
3396 : //---------------------------------------------------------
3397 : // accessibleInfo
3398 : //---------------------------------------------------------
3399 :
3400 0 : String Note::accessibleInfo() const
3401 : {
3402 0 : if (!chord() || !staff()) {
3403 0 : return String();
3404 : }
3405 :
3406 0 : String duration = chord()->durationUserName();
3407 0 : String voice = muse::mtrc("engraving", "Voice: %1").arg(track() % VOICES + 1);
3408 0 : String pitchName;
3409 0 : String onofftime;
3410 0 : if (!m_playEvents.empty()) {
3411 0 : int on = m_playEvents[0].ontime();
3412 0 : int off = m_playEvents[0].offtime();
3413 0 : if (on != 0 || off != NoteEvent::NOTE_LENGTH) {
3414 : //: Note-on and note-off times relative to note duration, expressed in thousandths (per mille)
3415 0 : onofftime = u" " + muse::mtrc("engraving", "(on %1‰ off %2‰)").arg(on, off);
3416 : }
3417 : }
3418 :
3419 0 : const Drumset* drumset = originPart()->instrument(chord()->tick())->drumset();
3420 0 : if (fixed() && headGroup() == NoteHeadGroup::HEAD_SLASH) {
3421 0 : pitchName = chord()->noStem() ? muse::mtrc("engraving", "Beat slash") : muse::mtrc("engraving", "Rhythm slash");
3422 0 : } else if (staff()->isDrumStaff(tick()) && drumset) {
3423 0 : pitchName = drumset->translatedName(pitch());
3424 0 : } else if (staff()->isTabStaff(tick())) {
3425 0 : pitchName = muse::mtrc("engraving", "%1; String: %2; Fret: %3")
3426 0 : .arg(tpcUserName(false), String::number(string() + 1), String::number(fret()));
3427 : } else {
3428 0 : pitchName = tpcUserName(false);
3429 : }
3430 :
3431 0 : return muse::mtrc("engraving", "%1; Pitch: %2; Duration: %3%4%5")
3432 0 : .arg(noteTypeUserName(), pitchName, duration, onofftime, (chord()->isGrace() ? u"" : String(u"; %1").arg(voice)));
3433 0 : }
3434 :
3435 : //---------------------------------------------------------
3436 : // screenReaderInfo
3437 : //---------------------------------------------------------
3438 :
3439 0 : String Note::screenReaderInfo() const
3440 : {
3441 0 : const Part* part = originPart();
3442 0 : if (!part) {
3443 0 : return String(); // part is nullptr on Linux after an instrument is deleted.
3444 : }
3445 :
3446 0 : const Fraction tick = this->tick();
3447 0 : const Chord* chord = this->chord();
3448 0 : const Instrument* instrument = part->instrument(tick);
3449 :
3450 0 : IF_ASSERT_FAILED(chord && instrument) {
3451 0 : return String();
3452 : }
3453 :
3454 0 : String duration = chord->durationUserName();
3455 0 : Measure* m = chord->measure();
3456 0 : bool voices = m ? m->hasVoices(staffIdx()) : false;
3457 0 : String voice = voices ? muse::mtrc("engraving", "Voice: %1").arg(track() % VOICES + 1) : u"";
3458 0 : String pitchName;
3459 0 : String pitchOutOfRangeWarning;
3460 0 : if (fixed() && headGroup() == NoteHeadGroup::HEAD_SLASH) {
3461 0 : pitchName = chord->noStem() ? muse::mtrc("engraving", "Beat slash") : muse::mtrc("engraving", "Rhythm slash");
3462 0 : } else if (const Drumset* d = instrument->drumset(); d&& instrument->useDrumset()) {
3463 0 : pitchName = d->translatedName(m_pitch);
3464 0 : } else if (const Staff* staff = this->staff(); staff&& staff->isTabStaff(tick)) {
3465 0 : pitchName = muse::mtrc("engraving", "%1; String: %2; Fret: %3")
3466 0 : .arg(tpcUserName(true, true), String::number(string() + 1), String::number(fret()));
3467 : } else {
3468 0 : pitchName = m_headGroup == NoteHeadGroup::HEAD_NORMAL
3469 0 : ? tpcUserName(true, true)
3470 : //: head as in note head. %1 is head type (circle, cross, etc.). %2 is pitch (e.g. Db4).
3471 0 : : muse::mtrc("engraving", "%1 head %2").arg(translatedSubtypeUserName()).arg(tpcUserName(true));
3472 0 : if (chord->staffMove() < 0) {
3473 0 : duration += u"; " + muse::mtrc("engraving", "Cross-staff above");
3474 0 : } else if (chord->staffMove() > 0) {
3475 0 : duration += u"; " + muse::mtrc("engraving", "Cross-staff below");
3476 : }
3477 :
3478 0 : if (m_pitch < instrument->minPitchP()) {
3479 0 : pitchOutOfRangeWarning = u" " + muse::mtrc("engraving", "too low");
3480 0 : } else if (m_pitch > instrument->maxPitchP()) {
3481 0 : pitchOutOfRangeWarning = u" " + muse::mtrc("engraving", "too high");
3482 0 : } else if (m_pitch < instrument->minPitchA()) {
3483 0 : pitchOutOfRangeWarning = u" " + muse::mtrc("engraving", "too low for amateurs");
3484 0 : } else if (m_pitch > instrument->maxPitchA()) {
3485 0 : pitchOutOfRangeWarning = u" " + muse::mtrc("engraving", "too high for amateurs");
3486 : }
3487 : }
3488 0 : return String(u"%1 %2 %3%4%5").arg(noteTypeUserName(), pitchName, duration, pitchOutOfRangeWarning,
3489 0 : (chord->isGrace() ? u"" : String(u"; %1").arg(voice)));
3490 0 : }
3491 :
3492 : //---------------------------------------------------------
3493 : // accessibleExtraInfo
3494 : //---------------------------------------------------------
3495 :
3496 0 : String Note::accessibleExtraInfo() const
3497 : {
3498 0 : String rez;
3499 0 : if (accidental()) {
3500 0 : rez = String(u"%1 %2").arg(rez, accidental()->screenReaderInfo());
3501 : }
3502 0 : if (!el().empty()) {
3503 0 : for (EngravingItem* e : el()) {
3504 0 : if (!score()->selectionFilter().canSelect(e)) {
3505 0 : continue;
3506 : }
3507 0 : rez = String(u"%1 %2").arg(rez, e->screenReaderInfo());
3508 : }
3509 : }
3510 0 : if (tieFor()) {
3511 0 : rez += u" " + muse::mtrc("engraving", "Start of %1").arg(tieFor()->screenReaderInfo());
3512 : }
3513 :
3514 0 : if (tieBack()) {
3515 0 : rez += u" " + muse::mtrc("engraving", "End of %1").arg(tieBack()->screenReaderInfo());
3516 : }
3517 :
3518 0 : if (!spannerFor().empty()) {
3519 0 : for (Spanner* s : spannerFor()) {
3520 0 : if (!score()->selectionFilter().canSelect(s)) {
3521 0 : continue;
3522 : }
3523 0 : rez += u" " + muse::mtrc("engraving", "Start of %1").arg(s->screenReaderInfo());
3524 : }
3525 : }
3526 0 : if (!spannerBack().empty()) {
3527 0 : for (Spanner* s : spannerBack()) {
3528 0 : if (!score()->selectionFilter().canSelect(s)) {
3529 0 : continue;
3530 : }
3531 0 : rez += u" " + muse::mtrc("engraving", "End of %1").arg(s->screenReaderInfo());
3532 : }
3533 : }
3534 :
3535 : // only read extra information for top note of chord
3536 : // (it is reached directly on next/previous element)
3537 0 : if (this == chord()->upNote()) {
3538 0 : rez = String(u"%1 %2").arg(rez, chord()->accessibleExtraInfo());
3539 : }
3540 :
3541 0 : return rez;
3542 0 : }
3543 :
3544 100 : NoteVal Note::noteVal() const
3545 : {
3546 100 : NoteVal nval;
3547 100 : nval.pitch = pitch();
3548 100 : nval.velocityOverride = userVelocity();
3549 100 : nval.tpc1 = tpc1();
3550 100 : nval.tpc2 = tpc2();
3551 100 : nval.fret = fret();
3552 100 : nval.string = string();
3553 100 : nval.headGroup = headGroup();
3554 :
3555 100 : return nval;
3556 : }
3557 :
3558 : //---------------------------------------------------------
3559 : // subtypeUserName
3560 : //---------------------------------------------------------
3561 :
3562 0 : TranslatableString Note::subtypeUserName() const
3563 : {
3564 0 : return TConv::userName(m_headGroup);
3565 : }
3566 :
3567 : //---------------------------------------------------------
3568 : // nextInEl
3569 : // returns next element in _el
3570 : //---------------------------------------------------------
3571 :
3572 0 : EngravingItem* Note::nextInEl(EngravingItem* e)
3573 : {
3574 0 : if (e == m_el.back()) {
3575 0 : return nullptr;
3576 : }
3577 0 : auto i = std::find(m_el.begin(), m_el.end(), e);
3578 0 : if (i == m_el.end()) {
3579 0 : return nullptr;
3580 : }
3581 0 : return *(i + 1);
3582 : }
3583 :
3584 : //---------------------------------------------------------
3585 : // prevInEl
3586 : // returns prev element in _el
3587 : //---------------------------------------------------------
3588 :
3589 0 : EngravingItem* Note::prevInEl(EngravingItem* e)
3590 : {
3591 0 : if (e == m_el.front()) {
3592 0 : return nullptr;
3593 : }
3594 0 : auto i = std::find(m_el.begin(), m_el.end(), e);
3595 0 : if (i == m_el.end()) {
3596 0 : return nullptr;
3597 : }
3598 0 : return *(i - 1);
3599 : }
3600 :
3601 0 : static bool tieValid(Tie* tie)
3602 : {
3603 0 : return tie && !tie->segmentsEmpty();
3604 : }
3605 :
3606 : //---------------------------------------------------------
3607 : // nextElement
3608 : //---------------------------------------------------------
3609 :
3610 0 : EngravingItem* Note::nextElement()
3611 : {
3612 0 : EngravingItem* e = score()->selection().element();
3613 0 : if (!e && !score()->selection().elements().empty()) {
3614 0 : e = score()->selection().elements().front();
3615 : }
3616 0 : if (!e) {
3617 0 : return nullptr;
3618 : }
3619 0 : switch (e->type()) {
3620 0 : case ElementType::SYMBOL:
3621 : case ElementType::IMAGE:
3622 : case ElementType::FINGERING:
3623 : case ElementType::TEXT:
3624 : case ElementType::BEND: {
3625 0 : EngravingItem* next = nextInEl(e); // return next element in _el
3626 0 : if (next) {
3627 0 : return next;
3628 0 : } else if (tieValid(m_tieFor)) {
3629 0 : return m_tieFor->frontSegment();
3630 0 : } else if (!m_spannerFor.empty()) {
3631 0 : for (auto i : m_spannerFor) {
3632 0 : if (i->isGlissando()) {
3633 0 : return i->spannerSegments().front();
3634 : }
3635 : }
3636 : }
3637 0 : return nullptr;
3638 : }
3639 :
3640 0 : case ElementType::TIE_SEGMENT:
3641 : case ElementType::LAISSEZ_VIB_SEGMENT:
3642 : case ElementType::PARTIAL_TIE_SEGMENT:
3643 0 : if (!m_spannerFor.empty()) {
3644 0 : for (auto i : m_spannerFor) {
3645 0 : if (i->isGlissando()) {
3646 0 : return i->spannerSegments().front();
3647 : }
3648 : }
3649 : }
3650 0 : return chord()->nextElement();
3651 :
3652 0 : case ElementType::NOTELINE_SEGMENT:
3653 : case ElementType::GLISSANDO_SEGMENT:
3654 : case ElementType::GUITAR_BEND_SEGMENT:
3655 0 : return chord()->nextElement();
3656 :
3657 0 : case ElementType::ACCIDENTAL:
3658 0 : if (!m_el.empty()) {
3659 0 : return m_el[0];
3660 : }
3661 0 : if (tieValid(m_tieFor)) {
3662 0 : return m_tieFor->frontSegment();
3663 : }
3664 0 : if (!m_spannerFor.empty()) {
3665 0 : for (auto i : m_spannerFor) {
3666 0 : if (i->isGlissando()) {
3667 0 : return i->spannerSegments().front();
3668 : }
3669 : }
3670 : }
3671 0 : return nullptr;
3672 :
3673 0 : case ElementType::NOTE: {
3674 0 : if (isPreBendOrDiveStart() || isGraceBendStart()) {
3675 0 : return bendFor()->frontSegment();
3676 : }
3677 :
3678 0 : GraceNotesGroup& graceNotesAfter = chord()->graceNotesAfter();
3679 0 : if (!graceNotesAfter.empty()) {
3680 0 : Chord* graceNotesAfterFirstChord = graceNotesAfter.front();
3681 0 : if (graceNotesAfterFirstChord) {
3682 0 : return graceNotesAfterFirstChord->notes().front();
3683 : }
3684 : }
3685 :
3686 0 : if (!m_el.empty()) {
3687 0 : return m_el[0];
3688 : }
3689 0 : if (tieValid(m_tieFor)) {
3690 0 : return m_tieFor->frontSegment();
3691 : }
3692 0 : if (!m_spannerFor.empty()) {
3693 0 : for (auto i : m_spannerFor) {
3694 0 : if (i->isGlissando() || i->isGuitarBend()) {
3695 0 : return i->spannerSegments().front();
3696 : }
3697 : }
3698 : }
3699 0 : return nullptr;
3700 : }
3701 0 : default:
3702 0 : return nullptr;
3703 : }
3704 : }
3705 :
3706 : //---------------------------------------------------------
3707 : // prevElement
3708 : //---------------------------------------------------------
3709 :
3710 0 : EngravingItem* Note::prevElement()
3711 : {
3712 0 : EngravingItem* e = score()->selection().element();
3713 0 : if (!e && !score()->selection().elements().empty()) {
3714 0 : e = score()->selection().elements().back();
3715 : }
3716 0 : if (!e) {
3717 0 : return nullptr;
3718 : }
3719 0 : switch (e->type()) {
3720 0 : case ElementType::SYMBOL:
3721 : case ElementType::IMAGE:
3722 : case ElementType::FINGERING:
3723 : case ElementType::TEXT:
3724 : case ElementType::BEND: {
3725 0 : EngravingItem* prev = prevInEl(e); // return prev element in _el
3726 0 : if (prev) {
3727 0 : return prev;
3728 : }
3729 : }
3730 0 : return this;
3731 0 : case ElementType::TIE_SEGMENT:
3732 : case ElementType::PARTIAL_TIE_SEGMENT:
3733 : case ElementType::LAISSEZ_VIB_SEGMENT:
3734 0 : if (!m_el.empty()) {
3735 0 : return m_el.back();
3736 : }
3737 0 : return this;
3738 0 : case ElementType::NOTELINE_SEGMENT:
3739 : case ElementType::GLISSANDO_SEGMENT:
3740 : case ElementType::GUITAR_BEND_SEGMENT:
3741 0 : if (tieValid(m_tieFor)) {
3742 0 : return m_tieFor->frontSegment();
3743 0 : } else if (!m_el.empty()) {
3744 0 : return m_el.back();
3745 : }
3746 0 : return this;
3747 0 : case ElementType::ACCIDENTAL:
3748 0 : return this;
3749 0 : default:
3750 0 : return nullptr;
3751 : }
3752 : }
3753 :
3754 : //---------------------------------------------------------
3755 : // lastElementBeforeSegment
3756 : //---------------------------------------------------------
3757 :
3758 0 : EngravingItem* Note::lastElementBeforeSegment()
3759 : {
3760 0 : if (!m_spannerFor.empty()) {
3761 0 : for (auto i : m_spannerFor) {
3762 0 : if (i->isGlissando() || i->isGuitarBend() || i->isNoteLine()) {
3763 0 : return i->spannerSegments().front();
3764 : }
3765 : }
3766 : }
3767 0 : if (tieValid(m_tieFor)) {
3768 0 : return m_tieFor->frontSegment();
3769 : }
3770 0 : if (!m_el.empty()) {
3771 0 : return m_el.back();
3772 : }
3773 0 : return this;
3774 : }
3775 :
3776 : //---------------------------------------------------------
3777 : // nextSegmentElement
3778 : //---------------------------------------------------------
3779 :
3780 0 : EngravingItem* Note::nextSegmentElement()
3781 : {
3782 0 : if (chord()->isGrace()) {
3783 0 : return EngravingItem::nextSegmentElement();
3784 : }
3785 :
3786 0 : const std::vector<Note*>& notes = chord()->notes();
3787 0 : if (this == notes.front()) {
3788 0 : return chord()->nextSegmentElement();
3789 : }
3790 0 : auto i = std::find(notes.begin(), notes.end(), this);
3791 0 : return *(i - 1);
3792 : }
3793 :
3794 : //---------------------------------------------------------
3795 : // prevSegmentElement
3796 : //---------------------------------------------------------
3797 :
3798 0 : EngravingItem* Note::prevSegmentElement()
3799 : {
3800 0 : if (chord()->isGrace()) {
3801 0 : return EngravingItem::prevSegmentElement();
3802 : }
3803 :
3804 0 : const std::vector<Note*>& notes = chord()->notes();
3805 0 : if (this == notes.back()) {
3806 0 : return chord()->prevSegmentElement();
3807 : }
3808 0 : auto i = std::find(notes.begin(), notes.end(), this);
3809 0 : return *++i;
3810 : }
3811 :
3812 : //---------------------------------------------------------
3813 : // lastTiedNote
3814 : //---------------------------------------------------------
3815 :
3816 7539 : Note* Note::lastTiedNote(bool ignorePlayback) const
3817 : {
3818 7539 : std::vector<const Note*> notes;
3819 7539 : const Note* note = this;
3820 7539 : notes.push_back(note);
3821 7999 : while (note->tieFor() && (ignorePlayback || note->tieFor()->playSpanner())) {
3822 468 : if (std::find(notes.begin(), notes.end(), note->tieFor()->endNote()) != notes.end()) {
3823 0 : break;
3824 : }
3825 468 : if (!note->tieFor()->endNote()) {
3826 8 : break;
3827 : }
3828 460 : note = note->tieFor()->endNote();
3829 460 : notes.push_back(note);
3830 : }
3831 7539 : return const_cast<Note*>(note);
3832 7539 : }
3833 :
3834 : //---------------------------------------------------------
3835 : // firstTiedNote
3836 : // if note has ties, return last note in chain
3837 : // - handle recursion in connected notes
3838 : //---------------------------------------------------------
3839 :
3840 7624 : Note* Note::firstTiedNote(bool ignorePlayback) const
3841 : {
3842 7624 : std::vector<const Note*> notes;
3843 7624 : const Note* note = this;
3844 7624 : notes.push_back(note);
3845 8088 : while (note->tieBack() && (ignorePlayback || note->tieBack()->playSpanner())) {
3846 467 : if (std::find(notes.begin(), notes.end(), note->tieBack()->startNote()) != notes.end()) {
3847 0 : break;
3848 : }
3849 467 : if (!note->tieBack()->startNote()) {
3850 3 : break;
3851 : }
3852 464 : note = note->tieBack()->startNote();
3853 464 : notes.push_back(note);
3854 : }
3855 7624 : return const_cast<Note*>(note);
3856 7624 : }
3857 :
3858 : //---------------------------------------------------------
3859 : // tiedNotes
3860 : //---------------------------------------------------------
3861 :
3862 134 : std::vector<Note*> Note::findTiedNotes(Note* startNote, bool followPartialTies)
3863 : {
3864 : // Returns all notes ahead of startNote in a chain of ties
3865 : // Follows partial tie paths recursively
3866 134 : Note* note = startNote;
3867 134 : std::vector<Note*> notes;
3868 134 : notes.push_back(note);
3869 :
3870 137 : while (note->tieFor()) {
3871 3 : if (followPartialTies) {
3872 3 : for (TieJumpPoint* jumpPoint : *note->tieJumpPoints()) {
3873 0 : if (!jumpPoint->active() || jumpPoint->followingNote()) {
3874 0 : continue;
3875 : }
3876 0 : if (!jumpPoint->note() || std::find(notes.begin(), notes.end(), jumpPoint->note()) != notes.end()) {
3877 0 : continue;
3878 : }
3879 : // ONLY backtrack when end point is a full tie eg. around a segno
3880 0 : const bool endTieIsFullTie = jumpPoint->endTie() && !jumpPoint->endTie()->isPartialTie();
3881 0 : Note* jumpPointNote = endTieIsFullTie ? jumpPoint->endTie()->startNote()->firstTiedNote() : jumpPoint->note();
3882 0 : std::vector<Note*> partialTieNotes = findTiedNotes(jumpPointNote, !endTieIsFullTie);
3883 0 : notes.insert(notes.end(), partialTieNotes.begin(), partialTieNotes.end());
3884 0 : }
3885 : }
3886 :
3887 3 : Note* endNote = note->tieFor()->endNote();
3888 3 : if (!endNote || std::find(notes.begin(), notes.end(), endNote) != notes.end()) {
3889 0 : break;
3890 : }
3891 3 : note = endNote;
3892 3 : notes.push_back(note);
3893 : }
3894 :
3895 134 : return notes;
3896 0 : }
3897 :
3898 134 : std::vector<Note*> Note::tiedNotes() const
3899 : {
3900 : // Backtrack to the first tied note in a chain, then return all notes in the chain ahead of it
3901 134 : Note* note = firstTiedNote();
3902 :
3903 134 : std::vector<Note*> notes = findTiedNotes(note);
3904 :
3905 134 : return notes;
3906 : }
3907 :
3908 : //---------------------------------------------------------
3909 : // unisonIndex
3910 : //---------------------------------------------------------
3911 :
3912 869 : int Note::unisonIndex() const
3913 : {
3914 869 : int index = 0;
3915 1069 : for (Note* n : chord()->notes()) {
3916 1069 : if (n->pitch() == pitch()) {
3917 869 : if (n == this) {
3918 869 : return index;
3919 : } else {
3920 0 : ++index;
3921 : }
3922 : }
3923 : }
3924 0 : return 0;
3925 : }
3926 :
3927 : //---------------------------------------------------------
3928 : // disconnectTiedNotes
3929 : //---------------------------------------------------------
3930 :
3931 2703 : void Note::disconnectTiedNotes()
3932 : {
3933 2703 : if (tieBack() && tieBack()->startNote()) {
3934 0 : tieBack()->startNote()->remove(tieBack());
3935 : }
3936 2703 : if (tieFor() && tieFor()->endNote()) {
3937 0 : tieFor()->endNote()->setTieBack(0);
3938 : }
3939 2703 : }
3940 :
3941 : //---------------------------------------------------------
3942 : // connectTiedNotes
3943 : //---------------------------------------------------------
3944 :
3945 41532 : void Note::connectTiedNotes()
3946 : {
3947 41532 : if (tieBack() && !tieBack()->isPartialTie()) {
3948 43 : tieBack()->setEndNote(this);
3949 43 : if (tieBack()->startNote()) {
3950 43 : tieBack()->startNote()->add(tieBack());
3951 : }
3952 : }
3953 41532 : if (tieFor() && tieFor()->endNote() && !tieFor()->isPartialTie()) {
3954 0 : tieFor()->endNote()->setTieBack(tieFor());
3955 : }
3956 41532 : }
3957 :
3958 : //---------------------------------------------------------
3959 : // accidentalType
3960 : //---------------------------------------------------------
3961 :
3962 1432 : AccidentalType Note::accidentalType() const
3963 : {
3964 1432 : return m_accidental ? m_accidental->accidentalType() : AccidentalType::NONE;
3965 : }
3966 :
3967 : //---------------------------------------------------------
3968 : // setAccidentalType
3969 : //---------------------------------------------------------
3970 :
3971 0 : void Note::setAccidentalType(AccidentalType type)
3972 : {
3973 0 : if (score()) {
3974 0 : EditNote::changeAccidental(score(), this, type);
3975 : }
3976 0 : }
3977 :
3978 : //---------------------------------------------------------
3979 : // undoUnlink
3980 : //---------------------------------------------------------
3981 :
3982 181 : void Note::undoUnlink()
3983 : {
3984 181 : EngravingItem::undoUnlink();
3985 190 : for (EngravingItem* e : m_el) {
3986 9 : e->undoUnlink();
3987 : }
3988 181 : for (Spanner* s : m_spannerFor) {
3989 0 : s->undoUnlink();
3990 : }
3991 181 : if (m_tieFor) {
3992 8 : m_tieFor->undoUnlink();
3993 : }
3994 181 : }
3995 :
3996 : //---------------------------------------------------------
3997 : // slides
3998 : //---------------------------------------------------------
3999 :
4000 119 : static bool isSlideTo(Note::SlideType slideType)
4001 : {
4002 119 : return slideType == Note::SlideType::UpToNote || slideType == Note::SlideType::DownToNote;
4003 : }
4004 :
4005 58 : static bool isSlideFrom(Note::SlideType slideType)
4006 : {
4007 58 : return slideType == Note::SlideType::UpFromNote || slideType == Note::SlideType::DownFromNote;
4008 : }
4009 :
4010 119 : void Note::attachSlide(SlideType slideType)
4011 : {
4012 119 : if (isSlideTo(slideType)) {
4013 61 : m_slideToType = slideType;
4014 58 : } else if (isSlideFrom(slideType)) {
4015 58 : m_slideFromType = slideType;
4016 : }
4017 119 : }
4018 :
4019 336 : bool Note::hasSlideToNote() const
4020 : {
4021 336 : return m_slideToType != SlideType::Undefined;
4022 : }
4023 :
4024 719 : bool Note::hasSlideFromNote() const
4025 : {
4026 719 : return m_slideFromType != SlideType::Undefined;
4027 : }
4028 :
4029 148 : void Note::setParenthesesMode(const ParenthesesMode& v, bool addToLinked, bool generated)
4030 : {
4031 148 : IF_ASSERT_FAILED(v == ParenthesesMode::BOTH || v == ParenthesesMode::NONE) {
4032 0 : LOGE() << "Notes cannot set left & right parens individually";
4033 0 : return;
4034 : }
4035 :
4036 148 : if ((m_hasParens && v == ParenthesesMode::BOTH) || (!m_hasParens && v == ParenthesesMode::NONE)) {
4037 50 : return;
4038 : }
4039 :
4040 98 : const NoteParenthesisInfo* noteParenInfo = parenthesisInfo();
4041 :
4042 98 : Parenthesis* leftParen = noteParenInfo ? noteParenInfo->leftParen() : nullptr;
4043 :
4044 98 : const bool hasGeneratedParen = leftParen && leftParen->generated();
4045 98 : const bool hasUserParen = leftParen && !leftParen->generated();
4046 :
4047 98 : bool hasParen = v == ParenthesesMode::BOTH;
4048 :
4049 98 : if (generated && hasParen == hasGeneratedParen) {
4050 0 : return;
4051 : }
4052 :
4053 98 : if (!generated && hasParen == hasUserParen) {
4054 0 : return;
4055 : }
4056 :
4057 98 : m_hasParens = hasParen;
4058 :
4059 98 : if (hasParen) {
4060 294 : EditChord::addChordParentheses(chord(), { this }, addToLinked, generated);
4061 : } else {
4062 0 : EditChord::removeChordParentheses(chord(), { this }, addToLinked, generated);
4063 : }
4064 : }
4065 :
4066 71744 : const NoteParenthesisInfo* Note::parenthesisInfo() const
4067 : {
4068 71744 : return chord() ? chord()->findNoteParenthesisInfo(this) : nullptr;
4069 : }
4070 :
4071 1457956 : bool Note::isGrace() const
4072 : {
4073 1457956 : return noteType() != NoteType::NORMAL;
4074 : }
4075 :
4076 392 : bool Note::isPreBendOrDiveStart() const
4077 : {
4078 392 : if (!isGrace()) {
4079 269 : return false;
4080 : }
4081 :
4082 123 : const GuitarBend* bend = bendFor();
4083 123 : if (!bend) {
4084 84 : return false;
4085 : }
4086 :
4087 39 : const GuitarBendType bendType = bend->bendType();
4088 39 : return bendType == GuitarBendType::PRE_BEND || bendType == GuitarBendType::PRE_DIVE;
4089 : }
4090 :
4091 328 : bool Note::isGraceBendStart() const
4092 : {
4093 328 : if (!isGrace()) {
4094 257 : return false;
4095 : }
4096 :
4097 71 : const GuitarBend* bend = bendFor();
4098 :
4099 71 : return bend && bend->bendType() == GuitarBendType::GRACE_NOTE_BEND;
4100 : }
4101 :
4102 409 : bool Note::isContinuationOfBend() const
4103 : {
4104 409 : if (bendBack()) {
4105 4 : return true;
4106 : }
4107 :
4108 405 : Tie* tie = tieBack();
4109 405 : Note* note = nullptr;
4110 976 : while (tie && tie->startNote()) {
4111 583 : note = tie->startNote();
4112 583 : if (note->bendBack()) {
4113 12 : return true;
4114 : }
4115 :
4116 571 : if (tie == note->tieBack()) {
4117 0 : return false;
4118 : }
4119 :
4120 571 : tie = note->tieBack();
4121 : }
4122 :
4123 393 : return false;
4124 : }
4125 :
4126 9107 : bool Note::hasAnotherStraightAboveOrBelow(bool above) const
4127 : {
4128 9107 : if (!chord()) {
4129 0 : return false;
4130 : }
4131 :
4132 9107 : const std::vector<Note*>& notes = chord()->notes();
4133 :
4134 9107 : if ((above && this == notes.back()) || (!above && this == notes.front())) {
4135 8568 : return false;
4136 : }
4137 :
4138 539 : const double limitDiff = 0.5 * spatium();
4139 1119 : for (Note* note : notes) {
4140 1111 : if (note == this) {
4141 154 : continue;
4142 : }
4143 957 : if (std::fabs(note->pos().x() - pos().x()) > limitDiff) {
4144 531 : return false;
4145 : }
4146 812 : if ((above && note->line() < m_line) || (!above && note->line() > m_line)) {
4147 386 : return true;
4148 : }
4149 : }
4150 :
4151 8 : return false;
4152 : }
4153 :
4154 12167 : PointF Note::posInStaffCoordinates()
4155 : {
4156 12167 : double X = x() + chord()->x() + chord()->segment()->x() + chord()->measure()->x();
4157 12167 : return PointF(X, y());
4158 : }
4159 :
4160 16 : void Note::setIsTrillCueNote(bool v)
4161 : {
4162 16 : m_isTrillCueNote = v;
4163 16 : if (chord()) {
4164 16 : chord()->setIsTrillCueNote(v);
4165 : }
4166 16 : }
4167 :
4168 12167 : void Note::addLineAttachPoint(PointF point, EngravingItem* line, bool start)
4169 : {
4170 : // IMPORTANT: the point is expected in *staff* coordinates
4171 : // We transform into note coordinates by subtracting the note position in staff coordinates
4172 12167 : point -= posInStaffCoordinates();
4173 12167 : m_lineAttachPoints.push_back(LineAttachPoint(line, point.x(), point.y(), start));
4174 12167 : }
4175 :
4176 5473 : bool Note::negativeFretUsed() const
4177 : {
4178 5473 : return configuration()->negativeFretsAllowed() && m_fret < 0 && m_string != INVALID_STRING_INDEX;
4179 : }
4180 :
4181 108005 : int Note::stringOrLine() const
4182 : {
4183 : // The number string() returns doesn't count spaces. This should be used where it is expected even numbers are spaces and odd are lines
4184 108005 : return staff()->staffType(tick())->isTabStaff() ? string() * 2 : line();
4185 : }
4186 :
4187 : //---------------------------------------------------------
4188 : // Note::transposeDiatonic
4189 : //---------------------------------------------------------
4190 :
4191 662 : bool Note::transposeDiatonic(int interval, bool keepAlterations, bool useDoubleAccidentals)
4192 : {
4193 : // compute note current absolute step
4194 : int alter;
4195 662 : Fraction tick = chord()->segment()->tick();
4196 662 : Key key = staff() ? staff()->key(tick) : Key::C;
4197 662 : int absStep = pitch2absStepByKey(epitch(), tpc(), key, alter);
4198 :
4199 : // get pitch and tcp corresponding to unaltered degree for this key
4200 662 : int newPitch = absStep2pitchByKey(absStep + interval, key);
4201 662 : int newTpc = step2tpcByKey((absStep + interval) % STEP_DELTA_OCTAVE, key);
4202 :
4203 662 : if (!pitchIsValid(newPitch)) {
4204 0 : return false;
4205 : }
4206 :
4207 : // if required, transfer original degree alteration to new pitch and tpc
4208 662 : if (keepAlterations) {
4209 232 : newPitch += alter;
4210 232 : newTpc += alter * TPC_DELTA_SEMITONE;
4211 : }
4212 :
4213 : // transpose appropriately
4214 662 : int newTpc1 = TPC_INVALID;
4215 662 : int newTpc2 = TPC_INVALID;
4216 662 : Interval v = staff() ? staff()->transpose(tick) : Interval(0);
4217 662 : if (concertPitch()) {
4218 0 : v.flip();
4219 0 : newTpc1 = clampEnharmonic(newTpc, useDoubleAccidentals);
4220 0 : newTpc2 = Transpose::transposeTpc(newTpc, v, useDoubleAccidentals);
4221 : } else {
4222 662 : newPitch += v.chromatic;
4223 662 : newTpc1 = Transpose::transposeTpc(newTpc, v, useDoubleAccidentals);
4224 662 : newTpc2 = clampEnharmonic(newTpc, useDoubleAccidentals);
4225 : }
4226 :
4227 : // check pitch is in range
4228 662 : newPitch = clampPitchOctaved(newPitch);
4229 :
4230 : // store new data
4231 662 : EditNote::undoChangePitch(score(), this, newPitch, newTpc1, newTpc2);
4232 662 : return true;
4233 : }
4234 :
4235 630 : bool Note::transpose(Interval interval, bool useDoubleSharpsFlats)
4236 : {
4237 630 : int npitch = pitch() + interval.chromatic;
4238 630 : if (!pitchIsValid(npitch)) {
4239 0 : return false;
4240 : }
4241 630 : int ntpc1 = Transpose::transposeTpc(tpc1(), interval, useDoubleSharpsFlats);
4242 630 : int ntpc2 = ntpc1;
4243 630 : if (transposition()) {
4244 6 : if (staff()) {
4245 6 : Interval v = staff()->transpose(tick());
4246 6 : v.flip();
4247 6 : ntpc2 = Transpose::transposeTpc(ntpc1, v, useDoubleSharpsFlats);
4248 : } else {
4249 0 : ntpc2 = Transpose::transposeTpc(tpc2(), interval, useDoubleSharpsFlats);
4250 : }
4251 : }
4252 630 : EditNote::undoChangePitch(score(), this, npitch, ntpc1, ntpc2);
4253 630 : return true;
4254 : }
4255 :
4256 134818 : staff_idx_t Note::vStaffIdx() const
4257 : {
4258 134818 : const Chord* c = chord();
4259 134818 : return c ? c->vStaffIdx() : EngravingItem::vStaffIdx();
4260 : }
4261 : }
|