1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2011 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "utils.h"
14 #include "score.h"
15 #include "pitchspelling.h"
16 #include "key.h"
17 #include "staff.h"
18 #include "note.h"
19 #include "harmony.h"
20 #include "segment.h"
21 #include "undo.h"
22 #include "keysig.h"
23 #include "stafftype.h"
24 #include "chord.h"
25 #include "measure.h"
26 #include "fret.h"
27 #include "part.h"
28
29 namespace Ms {
30
31 //---------------------------------------------------------
32 // keydiff2Interval
33 // keysig - -7(Cb) - +7(C#)
34 //---------------------------------------------------------
35
keydiff2Interval(Key oKey,Key nKey,TransposeDirection dir)36 static Interval keydiff2Interval(Key oKey, Key nKey, TransposeDirection dir)
37 {
38 static int stepTable[15] = {
39 // C G D A E B Fis
40 0, 4, 1, 5, 2, 6, 3,
41 };
42
43 int cofSteps; // circle of fifth steps
44 int diatonic;
45 if (nKey > oKey)
46 cofSteps = int(nKey) - int(oKey);
47 else
48 cofSteps = 12 - (int(oKey) - int(nKey));
49 diatonic = stepTable[(int(nKey) + 7) % 7] - stepTable[(int(oKey) + 7) % 7];
50 if (diatonic < 0)
51 diatonic += 7;
52 diatonic %= 7;
53 int chromatic = (cofSteps * 7) % 12;
54
55
56 if ((dir == TransposeDirection::CLOSEST) && (chromatic > 6))
57 dir = TransposeDirection::DOWN;
58
59 if (dir == TransposeDirection::DOWN) {
60 chromatic = chromatic - 12;
61 diatonic = diatonic - 7;
62 if (diatonic == -7)
63 diatonic = 0;
64 if (chromatic == -12)
65 chromatic = 0;
66 }
67 return Interval(diatonic, chromatic);
68 }
69
70 /*!
71 * Transposes both pitch and spelling for a note given an interval.
72 *
73 * Uses addition for pitch and transposeTpc() for spelling.
74 *
75 * @param pitch
76 * The initial (current) pitch. (pitch)
77 * @param tpc
78 * The initial spelling. (tpc)
79 * @param rpitch
80 * A pointer to the transposed pitch, calculated by this function. (pitch)
81 * @param rtpc
82 * A pointer to the transposed spelling. (tcp)
83 * @param interval
84 * The interval to transpose by.
85 * @param useDoubleSharpsFlats
86 * Determines whether the output may include double sharps or flats (Abb)
87 * or should use an enharmonic pitch (Abb = G).
88 */
89
transposeInterval(int pitch,int tpc,int * rpitch,int * rtpc,Interval interval,bool useDoubleSharpsFlats)90 void transposeInterval(int pitch, int tpc, int* rpitch, int* rtpc, Interval interval,
91 bool useDoubleSharpsFlats)
92 {
93 *rpitch = pitch + interval.chromatic;
94 *rtpc = transposeTpc(tpc, interval, useDoubleSharpsFlats);
95 }
96
97 /*!
98 * Transposes a pitch spelling given an interval.
99 *
100 * This function transposes a pitch spelling using first
101 * a diatonic transposition and then calculating any accidentals.
102 * This insures that the note is changed by the correct number of
103 * scale degrees unless it would require too many accidentals.
104 *
105 * @param tpc
106 * The initial pitch spelling.
107 * @param interval
108 * The interval to be transposed by.
109 * @param useDoubleSharpsFlats
110 * Determines whether the output may include double sharps or flats (Abb)
111 * or should use an enharmonic pitch (Abb = G).
112 *
113 * @return
114 * The transposed pitch spelling (tpc).
115 */
116
transposeTpc(int tpc,Interval interval,bool useDoubleSharpsFlats)117 int transposeTpc(int tpc, Interval interval, bool useDoubleSharpsFlats)
118 {
119 if (tpc == Tpc::TPC_INVALID) // perfect unison & perfect octave
120 return tpc;
121
122 int minAlter;
123 int maxAlter;
124 if (useDoubleSharpsFlats) {
125 minAlter = -2;
126 maxAlter = 2;
127 }
128 else {
129 minAlter = -1;
130 maxAlter = 1;
131 }
132 int steps = interval.diatonic;
133 int semitones = interval.chromatic;
134
135 // qDebug("transposeTpc tpc %d steps %d semitones %d", tpc, steps, semitones);
136 if (semitones == 0 && steps == 0)
137 return tpc;
138
139 int step;
140 int alter;
141 int pitch = tpc2pitch(tpc);
142
143 for (int k = 0; k < 10; ++k) {
144 step = tpc2step(tpc) + steps;
145 while (step < 0)
146 step += 7;
147 step %= 7;
148 int p1 = tpc2pitch(step2tpc(step, AccidentalVal::NATURAL));
149 alter = semitones - (p1 - pitch);
150 // alter = p1 + semitones - pitch;
151
152 // if (alter < 0) {
153 // alter *= -1;
154 // alter = 12 - alter;
155 // }
156 while (alter < 0)
157 alter += 12;
158
159 alter %= 12;
160 if (alter > 6)
161 alter -= 12;
162 if (alter > maxAlter)
163 ++steps;
164 else if (alter < minAlter)
165 --steps;
166 else
167 break;
168 // qDebug(" again alter %d steps %d, step %d", alter, steps, step);
169 }
170 // qDebug(" = step %d alter %d tpc %d", step, alter, step2tpc(step, alter));
171 return step2tpc(step, AccidentalVal(alter));
172 }
173
174 //---------------------------------------------------------
175 // transposeTpcDiatonicByKey
176 //
177 // returns the tpc diatonically transposed by steps, using degrees of given key
178 // option to keep any alteration tpc had with respect to unaltered corresponding degree of key
179 // option to enharmonically reduce tpc using double alterations
180 //---------------------------------------------------------
181
transposeTpcDiatonicByKey(int tpc,int steps,Key key,bool keepAlteredDegrees,bool useDoubleSharpsFlats)182 int transposeTpcDiatonicByKey(int tpc, int steps, Key key, bool keepAlteredDegrees, bool useDoubleSharpsFlats)
183 {
184 if (tpc == Tpc::TPC_INVALID)
185 return tpc;
186
187 // get step for tpc with alteration for key
188 int alter;
189 int step = tpc2stepByKey(tpc, key, alter);
190
191 // transpose step and get tpc for step/key
192 step += steps;
193 int newTpc = step2tpcByKey(step, key);
194
195 // if required, apply alteration to new tpc
196 if(keepAlteredDegrees)
197 newTpc += alter * TPC_DELTA_SEMITONE;
198
199 // check results are in ranges
200 while (newTpc > Tpc::TPC_MAX) newTpc -= TPC_DELTA_ENHARMONIC;
201 while (newTpc < Tpc::TPC_MIN) newTpc += TPC_DELTA_ENHARMONIC;
202
203 // if required, reduce double alterations
204 if(!useDoubleSharpsFlats) {
205 if(newTpc >= Tpc::TPC_F_SS) newTpc -= TPC_DELTA_ENHARMONIC;
206 if(newTpc <= Tpc::TPC_B_BB) newTpc += TPC_DELTA_ENHARMONIC;
207 }
208
209 return newTpc;
210 }
211
212 //---------------------------------------------------------
213 // transpose
214 // return false on failure
215 //---------------------------------------------------------
216
transpose(Note * n,Interval interval,bool useDoubleSharpsFlats)217 bool Score::transpose(Note* n, Interval interval, bool useDoubleSharpsFlats)
218 {
219 int npitch;
220 int ntpc1, ntpc2;
221 transposeInterval(n->pitch(), n->tpc1(), &npitch, &ntpc1, interval, useDoubleSharpsFlats);
222 if (n->transposition()) {
223 int p;
224 transposeInterval(n->pitch() - n->transposition(), n->tpc2(), &p, &ntpc2, interval, useDoubleSharpsFlats);
225 }
226 else
227 ntpc2 = ntpc1;
228 if (npitch > 127)
229 return false;
230 undoChangePitch(n, npitch, ntpc1, ntpc2);
231 return true;
232 }
233
234 //---------------------------------------------------------
235 // transpose
236 // return false on failure
237 //---------------------------------------------------------
238
transpose(TransposeMode mode,TransposeDirection direction,Key trKey,int transposeInterval,bool trKeys,bool transposeChordNames,bool useDoubleSharpsFlats)239 bool Score::transpose(TransposeMode mode, TransposeDirection direction, Key trKey,
240 int transposeInterval, bool trKeys, bool transposeChordNames, bool useDoubleSharpsFlats)
241 {
242 bool rangeSelection = selection().isRange();
243 int startStaffIdx = 0;
244 int endStaffIdx = 0;
245 Fraction startTick = Fraction(0,1);
246 if (rangeSelection) {
247 startStaffIdx = selection().staffStart();
248 endStaffIdx = selection().staffEnd();
249 startTick = selection().tickStart();
250 }
251
252 Staff* st = staff(startStaffIdx);
253
254 Interval interval;
255 if (mode != TransposeMode::DIATONICALLY) {
256 if (mode == TransposeMode::TO_KEY) {
257 // calculate interval from "transpose to key"
258 // find the key of the first pitched staff
259 Key key = Key::C;
260 for (int i = startStaffIdx; i < endStaffIdx; ++i) {
261 Staff* s = staff(i);
262 if (s->isPitchedStaff(startTick)) {
263 key = s->key(startTick);
264 if (!styleB(Sid::concertPitch)) {
265 int diff = s->part()->instrument(startTick)->transpose().chromatic;
266 if (diff)
267 key = transposeKey(key, diff, s->part()->preferSharpFlat());
268 }
269 // remember this staff to use as basis in transposing key signatures
270 st = s;
271 break;
272 }
273 }
274 if (key != trKey) {
275 interval = keydiff2Interval(key, trKey, direction);
276 }
277 else { //same key, which direction?
278 if (direction == TransposeDirection::UP)
279 interval = Interval(12);
280 else if (direction == TransposeDirection::DOWN)
281 interval = Interval(-12);
282 else //don't do anything for same key and closest direction
283 return true;
284 }
285 }
286 else {
287 interval = intervalList[transposeInterval];
288 if (direction == TransposeDirection::DOWN)
289 interval.flip();
290 }
291
292 if (!rangeSelection) {
293 trKeys = false;
294 }
295 bool fullOctave = (interval.chromatic % 12) == 0;
296 if (fullOctave && (mode != TransposeMode::TO_KEY)) {
297 trKeys = false;
298 transposeChordNames = false;
299 }
300 }
301 else { // diatonic transposition
302 if (direction == TransposeDirection::DOWN)
303 transposeInterval *= -1;
304 }
305
306 if (_selection.isList()) {
307 foreach (Element* e, _selection.uniqueElements()) {
308 if (!e->staff() || e->staff()->staffType(e->tick())->group() == StaffGroup::PERCUSSION)
309 continue;
310 if (e->isNote()) {
311 Note* note = toNote(e);
312 if (mode == TransposeMode::DIATONICALLY)
313 note->transposeDiatonic(transposeInterval, trKeys, useDoubleSharpsFlats);
314 else {
315 if (!transpose(note, interval, useDoubleSharpsFlats))
316 return false;
317 }
318 }
319 else if (e->isHarmony() && transposeChordNames) {
320 Harmony* h = toHarmony(e);
321 int rootTpc, baseTpc;
322 if (mode == TransposeMode::DIATONICALLY) {
323 Fraction tick = Fraction(0,1);
324 if (h->parent()->isSegment())
325 tick = toSegment(h->parent())->tick();
326 else if (h->parent()->isFretDiagram() && h->parent()->parent()->isSegment())
327 tick = toSegment(h->parent()->parent())->tick();
328 Key key = !h->staff() ? Key::C : h->staff()->key(tick);
329 rootTpc = transposeTpcDiatonicByKey(h->rootTpc(),
330 transposeInterval, key, trKeys, useDoubleSharpsFlats);
331 baseTpc = transposeTpcDiatonicByKey(h->baseTpc(),
332 transposeInterval, key, trKeys, useDoubleSharpsFlats);
333 }
334 else {
335 rootTpc = transposeTpc(h->rootTpc(), interval, useDoubleSharpsFlats);
336 baseTpc = transposeTpc(h->baseTpc(), interval, useDoubleSharpsFlats);
337 }
338 undoTransposeHarmony(h, rootTpc, baseTpc);
339 }
340 else if (e->isKeySig() && mode != TransposeMode::DIATONICALLY && trKeys) {
341 // TODO: this currently is disabled in dialog
342 // if we enabled it, then it will need work
343 // probably the code should look more like the range selection code
344 KeySig* ks = toKeySig(e);
345 if (!ks->isCustom() && !ks->isAtonal()) {
346 Key key = st->key(ks->tick());
347 KeySigEvent ke = ks->keySigEvent();
348 ke.setKey(key);
349 undo(new ChangeKeySig(ks, ke, ks->showCourtesy()));
350 }
351 }
352 }
353 return true;
354 }
355
356 //--------------------------
357 // process range selection
358 //--------------------------
359
360 QList<Staff*> sl;
361 for (int staffIdx = _selection.staffStart(); staffIdx < _selection.staffEnd(); ++staffIdx) {
362 Staff* s = staff(staffIdx);
363 if (s->staffType(Fraction(0,1))->group() == StaffGroup::PERCUSSION) // ignore percussion staff
364 continue;
365 if (sl.contains(s))
366 continue;
367 bool alreadyThere = false;
368 for (Staff* s2 : sl) {
369 if (s2 == s || (s2->links() && s2->links()->contains(s))) {
370 alreadyThere = true;
371 break;
372 }
373 }
374 if (!alreadyThere)
375 sl.append(s);
376 }
377 QList<int> tracks;
378 for (Staff* s : sl) {
379 int idx = s->idx() * VOICES;
380 for (int i = 0; i < VOICES; ++i)
381 tracks.append(idx + i);
382 }
383
384 Segment* s1 = _selection.startSegment();
385 // if range start on mmRest, get the actual segment instead
386 if (s1->measure()->isMMRest())
387 s1 = tick2segment(s1->tick(), true, s1->segmentType(), false);
388 // if range starts with first CR of measure
389 // then start looping from very beginning of measure
390 // so we include key signature and can transpose that if requested
391 if (s1->rtick().isZero())
392 s1 = s1->measure()->first();
393 Segment* s2 = _selection.endSegment();
394 for (Segment* segment = s1; segment && segment != s2; segment = segment->next1()) {
395 if (!segment->enabled())
396 continue;
397 for (int track : tracks) {
398 if (staff(track/VOICES)->staffType(s1->tick())->group() == StaffGroup::PERCUSSION)
399 continue;
400 Element* e = segment->element(track);
401 if (!e)
402 continue;
403
404 if (e->isChord()) {
405 Chord* chord = toChord(e);
406 std::vector<Note*> nl = chord->notes();
407 for (Note* n : nl) {
408 if (mode == TransposeMode::DIATONICALLY)
409 n->transposeDiatonic(transposeInterval, trKeys, useDoubleSharpsFlats);
410 else {
411 if (!transpose(n, interval, useDoubleSharpsFlats))
412 return false;
413 }
414 }
415 for (Chord* g : chord->graceNotes()) {
416 for (Note* n : g->notes()) {
417 if (mode == TransposeMode::DIATONICALLY)
418 n->transposeDiatonic(transposeInterval, trKeys, useDoubleSharpsFlats);
419 else {
420 if (!transpose(n, interval, useDoubleSharpsFlats))
421 return false;
422 }
423 }
424 }
425 }
426 else if (e->isKeySig() && trKeys && mode != TransposeMode::DIATONICALLY) {
427 KeySig* ks = toKeySig(e);
428 Fraction tick = segment->tick();
429 bool startKey = tick == s1->tick();
430 bool addKey = ks->isChange();
431 if ((startKey || addKey) && !ks->isCustom() && !ks->isAtonal()) {
432 Staff* staff = ks->staff();
433 Key oKey = ks->key();
434 if (!styleB(Sid::concertPitch)) {
435 Interval i = staff->part()->instrument(tick)->transpose();
436 // TODO: here we are converting the instrument-transposed key to concert pitch
437 // ideally, we would figure out how to get the original concert pitch key,
438 // since the current key may be affected by preferSharpsFlats() setting
439 // but ultimately it should not matter,
440 // because undoChangeKeySig() is going to change this again
441 oKey = transposeKey(oKey, i);
442 }
443 Key nKey = transposeKey(oKey, interval);
444 KeySigEvent ke = ks->keySigEvent();
445 ke.setKey(nKey);
446 // undoChangeKey handles linked staves/parts and generating new keysigs as needed
447 // it always sets the keysig non-generated
448 // so only call it when needed
449 undoChangeKeySig(staff, tick, ke);
450 }
451 }
452 }
453 if (transposeChordNames) {
454 foreach (Element* e, segment->annotations()) {
455 if ((e->type() != ElementType::HARMONY) || (!tracks.contains(e->track())))
456 continue;
457 Harmony* hh = toHarmony(e);
458 int rootTpc, baseTpc;
459 // undoTransposeHarmony does not do links
460 // because it is also used to handle transposing instruments
461 // and score / parts could be in different concert pitch states
462 for (ScoreElement* se : hh->linkList()) {
463 Harmony* h = toHarmony(se);
464 if (mode == TransposeMode::DIATONICALLY) {
465 Fraction tick = segment->tick();
466 Key key = !h->staff() ? Key::C : h->staff()->key(tick);
467 rootTpc = transposeTpcDiatonicByKey(h->rootTpc(),
468 transposeInterval, key, trKeys, useDoubleSharpsFlats);
469 baseTpc = transposeTpcDiatonicByKey(h->baseTpc(),
470 transposeInterval, key, trKeys, useDoubleSharpsFlats);
471 }
472 else {
473 rootTpc = transposeTpc(h->rootTpc(), interval, useDoubleSharpsFlats);
474 baseTpc = transposeTpc(h->baseTpc(), interval, useDoubleSharpsFlats);
475 }
476 undoTransposeHarmony(h, rootTpc, baseTpc);
477 }
478 }
479 }
480 }
481 //
482 // create missing key signatures
483 //
484 if (trKeys && (mode != TransposeMode::DIATONICALLY) && (s1->tick() == Fraction(0,1))) {
485 for (int track : tracks) {
486 if (track % VOICES)
487 continue;
488 Segment* seg = firstMeasure()->undoGetSegmentR(SegmentType::KeySig, Fraction(0,1));
489 KeySig* ks = toKeySig(seg->element(track));
490 if (!ks) {
491 ks = new KeySig(this);
492 ks->setTrack(track);
493 Key nKey = transposeKey(Key::C, interval, ks->part()->preferSharpFlat());
494 ks->setKey(nKey);
495 ks->setParent(seg);
496 undoAddElement(ks);
497 }
498 }
499 }
500 return true;
501 }
502
503 //---------------------------------------------------------
504 // transposeKeys
505 // key - -7(Cb) - +7(C#)
506 //---------------------------------------------------------
507
transposeKeys(int staffStart,int staffEnd,const Fraction & ts,const Fraction & tickEnd,const Interval & interval,bool useInstrument,bool flip)508 void Score::transposeKeys(int staffStart, int staffEnd, const Fraction& ts, const Fraction& tickEnd, const Interval& interval, bool useInstrument, bool flip)
509 {
510 Fraction tickStart(ts);
511 Interval firstInterval = interval;
512 Interval segmentInterval = interval;
513 if (tickStart < Fraction(0,1)) // -1 and 0 are valid values to indicate start of score
514 tickStart = Fraction(0,1);
515 for (int staffIdx = staffStart; staffIdx < staffEnd; ++staffIdx) {
516 Staff* st = staff(staffIdx);
517 if (st->staffType(tickStart)->group() == StaffGroup::PERCUSSION)
518 continue;
519
520 bool createKey = tickStart.isZero();
521 for (Segment* s = firstSegment(SegmentType::KeySig); s; s = s->next1(SegmentType::KeySig)) {
522 if (!s->enabled() || s->tick() < tickStart)
523 continue;
524 if (tickEnd != Fraction(-1,1) && s->tick() >= tickEnd)
525 break;
526 if (useInstrument) {
527 segmentInterval = st->part()->instrument(s->tick())->transpose();
528 if (flip)
529 segmentInterval.flip();
530 }
531 KeySig* ks = toKeySig(s->element(staffIdx * VOICES));
532 if (!ks || ks->generated())
533 continue;
534 if (s->tick().isZero())
535 createKey = false;
536 if (!ks->isCustom() && !ks->isAtonal()) {
537 KeySigEvent ke = st->keySigEvent(s->tick());
538 PreferSharpFlat pref = ks->part()->preferSharpFlat();
539 // TODO: if we are transposing to concert pitch,
540 // then instead of using the part preferSharpFlat() setting,
541 // we should somehow determine the actual concert pitch key
542 // eg, if concert pitch for the score as a whole is B, Eb instruments transpose to Ab
543 // it would be nice if when toggling concert pitch, those instruments were B, not Cb
544 // the code below makes a bold but unwarranted assumption:
545 // if you are using the prefer flats option,
546 // it's because you have sharps in your concert key signatures
547 // (and vice versa)
548 // better would be to check other non-transposing staves,
549 // but ideally we would record and save transposed and concert keys separately
550 // (like we do tpc1 & tpc2 for notes)
551 //if (useInstrument && !flip) {
552 // if (pref == PreferSharpFlat::FLATS)
553 // pref = PreferSharpFlat::SHARPS;
554 // else if (pref == PreferSharpFlat::SHARPS)
555 // pref = PreferSharpFlat::FLATS;
556 // }
557 Key nKey = transposeKey(ke.key(), segmentInterval, pref);
558 // remove initial C major key signatures
559 if (nKey == Key::C && s->tick().isZero()) {
560 undo(new RemoveElement(ks));
561 if (s->empty())
562 undo(new RemoveElement(s));
563 }
564 else {
565 ke.setKey(nKey);
566 undo(new ChangeKeySig(ks, ke, ks->showCourtesy()));
567 }
568 }
569 }
570 if (createKey && firstMeasure()) {
571 KeySig* ks = new KeySig(this);
572 ks->setTrack(staffIdx * VOICES);
573 Key nKey = transposeKey(Key::C, firstInterval, ks->part()->preferSharpFlat());
574 KeySigEvent ke;
575 ke.setKey(nKey);
576 ks->setKeySigEvent(ke);
577 Segment* seg = firstMeasure()->undoGetSegmentR(SegmentType::KeySig, Fraction(0,1));
578 seg->setHeader(true);
579 ks->setParent(seg);
580 undoAddElement(ks);
581 }
582 }
583 }
584
585 //---------------------------------------------------------
586 // transposeSemitone
587 //---------------------------------------------------------
588
transposeSemitone(int step)589 void Score::transposeSemitone(int step)
590 {
591 if (step == 0)
592 return;
593 if (step > 1)
594 step = 1;
595 if (step < -1)
596 step = -1;
597
598 TransposeDirection dir = step > 0 ? TransposeDirection::UP : TransposeDirection::DOWN;
599
600 int keyType = int(staff(0)->key(Fraction(0,1))) + 7; // ??
601
602 int intervalListArray[15][2] = {
603 // up - down
604 { 1, 1 }, // Cb
605 { 1, 1 }, // Gb
606 { 1, 1 }, // Db
607 { 1, 1 }, // Ab
608 { 1, 1 }, // Eb
609 { 1, 1 }, // Bb
610 { 1, 1 }, // F
611 { 1, 1 }, // C
612 { 1, 1 }, // G
613 { 1, 1 }, // D
614 { 1, 1 }, // A
615 { 1, 1 }, // E
616 { 1, 1 }, // B
617 { 1, 1 }, // F#
618 { 1, 1 } // C#
619 };
620
621 const int interval = intervalListArray[keyType][step > 0 ? 0 : 1];
622
623 if (!transpose(TransposeMode::BY_INTERVAL, dir, Key::C, interval, true, true, false)) {
624 qDebug("Score::transposeSemitone: failed");
625 // TODO: set error message
626 }
627 else setSelectionChanged(true);
628 }
629
630 //---------------------------------------------------------
631 // Note::transposeDiatonic
632 //---------------------------------------------------------
633
transposeDiatonic(int interval,bool keepAlterations,bool useDoubleAccidentals)634 void Note::transposeDiatonic(int interval, bool keepAlterations, bool useDoubleAccidentals)
635 {
636 // compute note current absolute step
637 int alter;
638 Fraction tick = chord()->segment()->tick();
639 Key key = staff() ? staff()->key(tick) : Key::C;
640 int absStep = pitch2absStepByKey(epitch(), tpc(), key, alter);
641
642 // get pitch and tcp corresponding to unaltered degree for this key
643 int newPitch = absStep2pitchByKey(absStep + interval, key);
644 int newTpc = step2tpcByKey((absStep + interval) % STEP_DELTA_OCTAVE, key);
645
646 // if required, transfer original degree alteration to new pitch and tpc
647 if (keepAlterations) {
648 newPitch += alter;
649 newTpc += alter * TPC_DELTA_SEMITONE;
650 }
651
652 // transpose appropriately
653 int newTpc1 = TPC_INVALID;
654 int newTpc2 = TPC_INVALID;
655 Interval v = staff() ? staff()->part()->instrument(tick)->transpose() : Interval(0);
656 if (concertPitch()) {
657 v.flip();
658 newTpc1 = newTpc;
659 newTpc2 = Ms::transposeTpc(newTpc, v, true);
660 }
661 else {
662 newPitch += v.chromatic;
663 newTpc1 = Ms::transposeTpc(newTpc, v, true);
664 newTpc2 = newTpc;
665 }
666
667 // check results are in ranges
668 while (newPitch > 127)
669 newPitch -= PITCH_DELTA_OCTAVE;
670 while (newPitch < 0)
671 newPitch += PITCH_DELTA_OCTAVE;
672 while (newTpc1 > Tpc::TPC_MAX)
673 newTpc1 -= TPC_DELTA_ENHARMONIC;
674 while (newTpc1 < Tpc::TPC_MIN)
675 newTpc1 += TPC_DELTA_ENHARMONIC;
676 while (newTpc2 > Tpc::TPC_MAX)
677 newTpc2 -= TPC_DELTA_ENHARMONIC;
678 while (newTpc2 < Tpc::TPC_MIN)
679 newTpc2 += TPC_DELTA_ENHARMONIC;
680
681 // if required, reduce double alterations
682 if (!useDoubleAccidentals) {
683 if (newTpc1 >= Tpc::TPC_F_SS)
684 newTpc1 -= TPC_DELTA_ENHARMONIC;
685 if (newTpc1 <= Tpc::TPC_B_BB)
686 newTpc1 += TPC_DELTA_ENHARMONIC;
687 if (newTpc2 >= Tpc::TPC_F_SS)
688 newTpc2 -= TPC_DELTA_ENHARMONIC;
689 if (newTpc2 <= Tpc::TPC_B_BB)
690 newTpc2 += TPC_DELTA_ENHARMONIC;
691 }
692
693 // store new data
694 score()->undoChangePitch(this, newPitch, newTpc1, newTpc2);
695 }
696
697 //---------------------------------------------------------
698 // transposeDiatonicAlterations
699 //---------------------------------------------------------
700
transposeDiatonicAlterations(TransposeDirection direction)701 void Score::transposeDiatonicAlterations(TransposeDirection direction)
702 {
703 // Transpose current selection diatonically (up/down) while keeping degree alterations
704 // Note: Score::transpose() absolutely requires valid selection before invocation.
705 if (!selection().isNone()) {
706 transpose(TransposeMode::DIATONICALLY, direction, Key::C, 1, true, true, true);
707 setPlayNote(true); // For when selection is a single note, also playback that note
708 setSelectionChanged(true); // This will update the on-screen keyboard
709 }
710 }
711
712 //---------------------------------------------------------
713 // transpositionChanged
714 //---------------------------------------------------------
715
transpositionChanged(Part * part,Interval oldV,Fraction tickStart,Fraction tickEnd)716 void Score::transpositionChanged(Part* part, Interval oldV, Fraction tickStart, Fraction tickEnd)
717 {
718 if (tickStart == Fraction(-1,1))
719 tickStart = Fraction(0,1);
720 Interval v = part->instrument(tickStart)->transpose();
721 v.flip();
722 Interval diffV(oldV.chromatic + v.chromatic);
723
724 // transpose keys first
725 QList<Score*> scores;
726 for (Staff* ls : part->staff(0)->staffList()) {
727 // TODO: special handling for linked staves within a score
728 // could be useful for capo
729 Score* score = ls->score();
730 if (scores.contains(score))
731 continue;
732 scores.append(score);
733 Part* lp = ls->part();
734 if (!score->styleB(Sid::concertPitch))
735 score->transposeKeys(lp->startTrack() / VOICES, lp->endTrack() / VOICES, tickStart, tickEnd, diffV);
736 }
737
738 // now transpose notes and chord symbols
739 for (Segment* s = firstSegment(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
740 if (s->tick() < tickStart)
741 continue;
742 if (tickEnd != Fraction(-1,1) && s->tick() >= tickEnd)
743 break;
744 for (Staff* st : *part->staves()) {
745 if (st->staffType(tickStart)->group() == StaffGroup::PERCUSSION)
746 continue;
747 int t1 = st->idx() * VOICES;
748 int t2 = t1 + VOICES;
749 for (int track = t1; track < t2; ++track) {
750 Element* e = s->element(track);
751 if (e && e->isChord()) {
752 Chord* c = toChord(e);
753 for (Chord* gc : c->graceNotes()) {
754 for (Note* n : gc->notes()) {
755 int tpc = transposeTpc(n->tpc1(), v, true);
756 n->undoChangeProperty(Pid::TPC2, tpc);
757 }
758 }
759 for (Note* n : c->notes()) {
760 int tpc = transposeTpc(n->tpc1(), v, true);
761 n->undoChangeProperty(Pid::TPC2, tpc);
762 }
763 }
764 // find chord symbols
765 for (Element* element : s->annotations()) {
766 if (element->track() != track || element->type() != ElementType::HARMONY)
767 continue;
768 Harmony* h = toHarmony(element);
769 int rootTpc = transposeTpc(h->rootTpc(), diffV, false);
770 int baseTpc = transposeTpc(h->baseTpc(), diffV, false);
771 for (ScoreElement* scoreElement : h->linkList()) {
772 if (!scoreElement->score()->styleB(Sid::concertPitch))
773 undoTransposeHarmony(toHarmony(scoreElement), rootTpc, baseTpc);
774 }
775 }
776 }
777 }
778 }
779 }
780 }
781
782