MuseScore/libmscore/transpose.cpp

576 lines
22 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include "utils.h"
#include "score.h"
#include "pitchspelling.h"
#include "key.h"
#include "staff.h"
#include "note.h"
#include "harmony.h"
#include "segment.h"
#include "undo.h"
#include "keysig.h"
#include "stafftype.h"
#include "chord.h"
#include "measure.h"
2012-12-10 21:15:28 +01:00
#include "fret.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// keydiff2Interval
// keysig - -7(Cb) - +7(C#)
//---------------------------------------------------------
static Interval keydiff2Interval(int oKey, int nKey, TransposeDirection dir)
{
static int stepTable[15] = {
// C G D A E B Fis
0, 4, 1, 5, 2, 6, 3,
};
int cofSteps; // circle of fifth steps
int diatonic;
if (nKey > oKey)
cofSteps = nKey - oKey;
else
cofSteps = 12 - (oKey - nKey);
diatonic = stepTable[(nKey + 7) % 7] - stepTable[(oKey + 7) % 7];
if (diatonic < 0)
diatonic += 7;
diatonic %= 7;
int chromatic = (cofSteps * 7) % 12;
if ((dir == TransposeDirection::CLOSEST) && (chromatic > 6))
dir = TransposeDirection::DOWN;
2012-05-26 14:26:10 +02:00
if (dir == TransposeDirection::DOWN) {
2012-05-26 14:26:10 +02:00
chromatic = chromatic - 12;
diatonic = diatonic - 7;
if (diatonic == -7)
diatonic = 0;
if (chromatic == -12)
chromatic = 0;
}
qDebug("TransposeByKey %d -> %d chromatic %d diatonic %d", oKey, nKey, chromatic, diatonic);
2012-05-26 14:26:10 +02:00
return Interval(diatonic, chromatic);
}
/*!
* Transposes both pitch and spelling for a note given an interval.
*
* Uses addition for pitch and transposeTpc() for spelling.
*
* @param pitch
* The initial (current) pitch. (pitch)
* @param tpc
* The initial spelling. (tpc)
* @param rpitch
* A pointer to the transposed pitch, calculated by this function. (pitch)
* @param rtpc
* A pointer to the transposed spelling. (tcp)
* @param interval
* The interval to transpose by.
* @param useDoubleSharpsFlats
* Determines whether the output may include double sharps or flats (Abb)
* or should use an enharmonic pitch (Abb = G).
*/
void transposeInterval(int pitch, int tpc, int* rpitch, int* rtpc, Interval interval,
bool useDoubleSharpsFlats)
{
*rpitch = pitch + interval.chromatic;
*rtpc = transposeTpc(tpc, interval, useDoubleSharpsFlats);
}
/*!
* Transposes a pitch spelling given an interval.
*
* This function transposes a pitch spelling using first
* a diatonic transposition and then calculating any accidentals.
* This insures that the note is changed by the correct number of
* scale degrees unless it would require too many accidentals.
*
* @param tpc
* The initial pitch spelling.
* @param interval
* The interval to be transposed by.
* @param useDoubleSharpsFlats
* Determines whether the output may include double sharps or flats (Abb)
* or should use an enharmonic pitch (Abb = G).
*
* @return
* The transposed pitch spelling (tpc).
*/
int transposeTpc(int tpc, Interval interval, bool useDoubleSharpsFlats)
{
if (tpc == Tpc::INVALID) // perfect unison & perfect octave
2012-05-26 14:26:10 +02:00
return tpc;
int minAlter;
int maxAlter;
if (useDoubleSharpsFlats) {
minAlter = -2;
maxAlter = 2;
}
else {
minAlter = -1;
maxAlter = 1;
}
int steps = interval.diatonic;
int semitones = interval.chromatic;
// qDebug("transposeTpc tpc %d steps %d semitones %d", tpc, steps, semitones);
2012-05-26 14:26:10 +02:00
if (semitones == 0 && steps == 0)
return tpc;
2012-08-08 20:46:29 +02:00
int step;
int alter;
2012-05-26 14:26:10 +02:00
int pitch = tpc2pitch(tpc);
for (int k = 0; k < 10; ++k) {
step = tpc2step(tpc) + steps;
while (step < 0)
step += 7;
step %= 7;
int p1 = tpc2pitch(step2tpc(step, AccidentalVal::NATURAL));
alter = int(AccidentalVal(semitones - (p1 - pitch)));
2012-05-26 14:26:10 +02:00
// alter = p1 + semitones - pitch;
if (alter < 0) {
alter *= -1;
alter = 12 - alter;
}
alter %= 12;
if (alter > 6)
alter -= 12;
if (alter > maxAlter)
++steps;
else if (alter < minAlter)
--steps;
else
break;
// qDebug(" again alter %d steps %d, step %d", alter, steps, step);
2012-05-26 14:26:10 +02:00
}
// qDebug(" = step %d alter %d tpc %d", step, alter, step2tpc(step, alter));
2012-08-08 20:46:29 +02:00
return step2tpc(step, AccidentalVal(alter));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// transposeTpcDiatonicByKey
//
// returns the tpc diatonically transposed by steps, using degrees of given key
// option to keep any alteration tpc had with respect to unaltered corresponding degree of key
// option to enharmonically reduce tpc using double alterations
//---------------------------------------------------------
int transposeTpcDiatonicByKey(int tpc, int steps, int key, bool keepAlteredDegrees, bool useDoubleSharpsFlats)
{
if (tpc == Tpc::INVALID)
return tpc;
// get step for tpc with alteration for key
int alter;
int step = tpc2stepByKey(tpc, key, &alter);
// transpose step and get tpc for step/key
step += steps;
int newTpc = step2tpcByKey(step, key);
// if required, apply alteration to new tpc
if(keepAlteredDegrees)
newTpc += alter * TPC_DELTA_SEMITONE;
// check results are in ranges
while (newTpc > Tpc::MAX) newTpc -= TPC_DELTA_ENHARMONIC;
while (newTpc < Tpc::MIN) newTpc += TPC_DELTA_ENHARMONIC;
// if required, reduce double alterations
if(!useDoubleSharpsFlats) {
if(newTpc >= Tpc::F_SS) newTpc -= TPC_DELTA_ENHARMONIC;
if(newTpc <= Tpc::B_BB) newTpc += TPC_DELTA_ENHARMONIC;
}
return newTpc;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// transposeStaff
//---------------------------------------------------------
void Score::cmdTransposeStaff(int staffIdx, Interval interval, bool useDoubleSharpsFlats)
{
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
if (staff(staffIdx)->staffType()->group() == PERCUSSION_STAFF_GROUP)
2012-05-26 14:26:10 +02:00
return;
int startTrack = staffIdx * VOICES;
int endTrack = startTrack + VOICES;
transposeKeys(staffIdx, staffIdx+1, 0, lastSegment()->tick(), interval);
for (Segment* segment = firstSegment(); segment; segment = segment->next1()) {
for (int st = startTrack; st < endTrack; ++st) {
Element* e = segment->element(st);
if (!e || e->type() != Element::ElementType::CHORD)
2012-05-26 14:26:10 +02:00
continue;
Chord* chord = static_cast<Chord*>(e);
QList<Note*> nl = chord->notes();
foreach(Note* n, nl)
transpose(n, interval, useDoubleSharpsFlats);
}
foreach (Element* e, segment->annotations()) {
if ((e->type() != Element::ElementType::HARMONY) || (e->track() < startTrack) || (e->track() >= endTrack))
2012-05-26 14:26:10 +02:00
continue;
Harmony* h = static_cast<Harmony*>(e);
int rootTpc = transposeTpc(h->rootTpc(), interval, false);
int baseTpc = transposeTpc(h->baseTpc(), interval, false);
undoTransposeHarmony(h, rootTpc, baseTpc);
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// transpose
//---------------------------------------------------------
void Score::transpose(Note* n, Interval interval, bool useDoubleSharpsFlats)
{
int npitch;
int ntpc1, ntpc2;
transposeInterval(n->pitch(), n->tpc1(), &npitch, &ntpc1, interval, useDoubleSharpsFlats);
2014-04-23 11:23:53 +02:00
if (n->transposition()) {
int p;
transposeInterval(n->pitch() - n->transposition(), n->tpc2(), &p, &ntpc2, interval, useDoubleSharpsFlats);
}
else
ntpc2 = ntpc1;
undoChangePitch(n, npitch, ntpc1, ntpc2);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// transpose
//---------------------------------------------------------
void Score::transpose(TransposeMode mode, TransposeDirection direction, int transposeKey,
2014-05-26 12:10:59 +02:00
int transposeInterval, bool trKeys, bool transposeChordNames, bool useDoubleSharpsFlats)
2012-05-26 14:26:10 +02:00
{
2014-05-26 13:21:04 +02:00
bool rangeSelection = selection().state() == SelState::RANGE;
2012-05-26 14:26:10 +02:00
int startStaffIdx = 0;
int startTick = 0;
if (rangeSelection) {
startStaffIdx = selection().staffStart();
startTick = selection().tickStart();
}
KeyList* km = staff(startStaffIdx)->keymap();
Interval interval;
if (mode != TransposeMode::DIATONICALLY) {
if (mode == TransposeMode::BY_KEY) {
// calculate interval from "transpose by key"
int oKey = km->key(startTick).accidentalType();
interval = keydiff2Interval(oKey, transposeKey, direction);
}
else {
interval = intervalList[transposeInterval];
if (direction == TransposeDirection::DOWN)
interval.flip();
}
2012-05-26 14:26:10 +02:00
if (!rangeSelection) {
trKeys = false;
}
bool fullOctave = (interval.chromatic % 12) == 0;
if (fullOctave && (mode != TransposeMode::BY_KEY)) {
trKeys = false;
transposeChordNames = false;
}
2012-05-26 14:26:10 +02:00
}
else // diatonic transposition
if (direction == TransposeDirection::DOWN)
transposeInterval *= -1;
2012-05-26 14:26:10 +02:00
2014-05-26 13:21:04 +02:00
if (_selection.state() == SelState::LIST) {
2014-05-26 12:10:59 +02:00
foreach(Element* e, _selection.uniqueElements()) {
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
if (e->staff()->staffType()->group() == PERCUSSION_STAFF_GROUP)
2012-05-26 14:26:10 +02:00
continue;
if (e->type() == Element::ElementType::NOTE) {
Note* note = static_cast<Note*>(e);
if (mode == TransposeMode::DIATONICALLY)
note->transposeDiatonic(transposeInterval, trKeys, useDoubleSharpsFlats);
else
transpose(note, interval, useDoubleSharpsFlats);
}
else if ((e->type() == Element::ElementType::HARMONY) && transposeChordNames) {
2012-05-26 14:26:10 +02:00
Harmony* h = static_cast<Harmony*>(e);
int rootTpc, baseTpc;
if (mode == TransposeMode::DIATONICALLY) {
2012-12-10 21:15:28 +01:00
int tick = 0;
if (h->parent()->type() == Element::ElementType::SEGMENT)
2012-12-10 21:15:28 +01:00
tick = static_cast<Segment*>(h->parent())->tick();
else if (h->parent()->type() == Element::ElementType::FRET_DIAGRAM
&& h->parent()->parent()->type() == Element::ElementType::SEGMENT) {
tick = static_cast<Segment*>(h->parent()->parent())->tick();
}
int key = !h->staff() ? Key::KEY_C : h->staff()->keymap()->key(tick).accidentalType();
rootTpc = transposeTpcDiatonicByKey(h->rootTpc(),
transposeInterval, key, trKeys, useDoubleSharpsFlats);
baseTpc = transposeTpcDiatonicByKey(h->baseTpc(),
transposeInterval, key, trKeys, useDoubleSharpsFlats);
}
else {
rootTpc = transposeTpc(h->rootTpc(), interval, false);
baseTpc = transposeTpc(h->baseTpc(), interval, false);
}
2012-05-26 14:26:10 +02:00
undoTransposeHarmony(h, rootTpc, baseTpc);
}
else if ((e->type() == Element::ElementType::KEYSIG) && mode != TransposeMode::DIATONICALLY && trKeys) {
2012-05-26 14:26:10 +02:00
KeySig* ks = static_cast<KeySig*>(e);
KeySigEvent key = km->key(ks->tick());
KeySigEvent okey = km->key(ks->tick() - 1);
key.setNaturalType(okey.accidentalType());
undo(new ChangeKeySig(ks, key, ks->showCourtesy() /*,
ks->showNaturals()*/));
2012-05-26 14:26:10 +02:00
}
}
return;
}
2014-05-26 12:10:59 +02:00
//--------------------------
// process range selection
//--------------------------
QList<Staff*> sl;
for (int staffIdx = _selection.staffStart(); staffIdx < _selection.staffEnd(); ++staffIdx) {
Staff* s = staff(staffIdx);
if (s->staffType()->group() == PERCUSSION_STAFF_GROUP) // ignore percussion staff
continue;
if (sl.contains(s))
continue;
bool alreadyThere = false;
for (Staff* s2 : sl) {
if (s2 == s || (s2->linkedStaves() && s2->linkedStaves()->staves().contains(s))) {
alreadyThere = true;
break;
}
}
if (!alreadyThere)
sl.append(s);
}
QList<int> tracks;
for (Staff* s : sl) {
int idx = s->idx() * VOICES;
for (int i = 0; i < VOICES; ++i)
tracks.append(idx + i);
}
2012-05-26 14:26:10 +02:00
for (Segment* segment = _selection.startSegment(); segment && segment != _selection.endSegment(); segment = segment->next1()) {
2014-05-26 12:10:59 +02:00
for (int st : tracks) {
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
if (staff(st/VOICES)->staffType()->group() == PERCUSSION_STAFF_GROUP)
2012-05-26 14:26:10 +02:00
continue;
Element* e = segment->element(st);
if (!e || e->type() != Element::ElementType::CHORD)
2012-05-26 14:26:10 +02:00
continue;
Chord* chord = static_cast<Chord*>(e);
QList<Note*> nl = chord->notes();
for (Note* n : nl) {
if (mode == TransposeMode::DIATONICALLY)
n->transposeDiatonic(transposeInterval, trKeys, useDoubleSharpsFlats);
else
transpose(n, interval, useDoubleSharpsFlats);
}
for (Chord* g : chord->graceNotes()) {
for (Note* n : g->notes()) {
if (mode == TransposeMode::DIATONICALLY)
n->transposeDiatonic(transposeInterval, trKeys, useDoubleSharpsFlats);
else
transpose(n, interval, useDoubleSharpsFlats);
}
}
2012-05-26 14:26:10 +02:00
}
if (transposeChordNames) {
foreach (Element* e, segment->annotations()) {
2014-05-26 12:10:59 +02:00
if ((e->type() != Element::ElementType::HARMONY) || (!tracks.contains(e->track())))
2012-05-26 14:26:10 +02:00
continue;
Harmony* h = static_cast<Harmony*>(e);
int rootTpc, baseTpc;
if (mode == TransposeMode::DIATONICALLY) {
int tick = segment->tick();
int key = !h->staff() ? Key::KEY_C : h->staff()->keymap()->key(tick).accidentalType();
rootTpc = transposeTpcDiatonicByKey(h->rootTpc(),
transposeInterval, key, trKeys, useDoubleSharpsFlats);
baseTpc = transposeTpcDiatonicByKey(h->baseTpc(),
transposeInterval, key, trKeys, useDoubleSharpsFlats);
}
else {
rootTpc = transposeTpc(h->rootTpc(), interval, false);
baseTpc = transposeTpc(h->baseTpc(), interval, false);
}
2012-05-26 14:26:10 +02:00
undoTransposeHarmony(h, rootTpc, baseTpc);
}
}
}
if (trKeys && mode != TransposeMode::DIATONICALLY) {
2012-05-26 14:26:10 +02:00
transposeKeys(_selection.staffStart(), _selection.staffEnd(),
_selection.tickStart(), _selection.tickEnd(), interval);
}
setLayoutAll(true);
}
//---------------------------------------------------------
// transposeKeys
// key - -7(Cb) - +7(C#)
//---------------------------------------------------------
void Score::transposeKeys(int staffStart, int staffEnd, int tickStart, int tickEnd, const Interval& interval)
{
for (int staffIdx = staffStart; staffIdx < staffEnd; ++staffIdx) {
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
if (staff(staffIdx)->staffType()->group() == PERCUSSION_STAFF_GROUP)
2012-05-26 14:26:10 +02:00
continue;
KeyList* km = staff(staffIdx)->keymap();
2014-04-10 13:13:37 +02:00
for (auto ke = km->lower_bound(tickStart); ke != km->lower_bound(tickEnd); ++ke) {
2012-05-26 14:26:10 +02:00
KeySigEvent oKey = ke->second;
int tick = ke->first;
int nKeyType = transposeKey(oKey.accidentalType(), interval);
KeySigEvent nKey;
nKey.setAccidentalType(nKeyType);
(*km)[tick] = nKey;
// undoChangeKey(staff(staffIdx), tick, oKey, nKey);
}
for (Segment* s = firstSegment(); s; s = s->next1()) {
if (s->segmentType() != Segment::SegKeySig)
2012-05-26 14:26:10 +02:00
continue;
if (s->tick() < tickStart)
continue;
if (s->tick() >= tickEnd)
break;
KeySig* ks = static_cast<KeySig*>(s->element(staffIdx * VOICES));
if (ks) {
KeySigEvent key = km->key(s->tick());
KeySigEvent okey = km->key(s->tick() - 1);
key.setNaturalType(okey.accidentalType());
undo(new ChangeKeySig(ks, key, ks->showCourtesy() /*,
ks->showNaturals()*/));
2012-05-26 14:26:10 +02:00
}
}
}
}
//---------------------------------------------------------
// transposeSemitone
//---------------------------------------------------------
void Score::transposeSemitone(int step)
{
if (step == 0)
return;
if (step > 1)
step = 1;
if (step < -1)
step = -1;
TransposeDirection dir = step > 0 ? TransposeDirection::UP : TransposeDirection::DOWN;
2012-05-26 14:26:10 +02:00
KeyList* km = staff(0)->keymap();
KeySigEvent key = km->lower_bound(0)->second;
int keyType = key.accidentalType() + 7;
int intervalList[15][2] = {
// up - down
{ 1, 1 }, // Cb
{ 1, 1 }, // Gb
{ 1, 1 }, // Db
{ 1, 1 }, // Ab
{ 1, 1 }, // Eb
{ 1, 1 }, // Bb
{ 1, 1 }, // F
{ 1, 1 }, // C
{ 1, 1 }, // G
{ 1, 1 }, // D
{ 1, 1 }, // A
{ 1, 1 }, // E
{ 1, 1 }, // B
{ 1, 1 }, // F#
{ 1, 1 } // C#
};
int interval = intervalList[keyType][step > 0 ? 0 : 1];
cmdSelectAll();
startCmd();
transpose(TransposeMode::BY_INTERVAL, dir, 0, interval, true, true, false);
2012-05-26 14:26:10 +02:00
deselectAll();
setLayoutAll(true);
endCmd();
}
//---------------------------------------------------------
// Note::transposeDiatonic
//---------------------------------------------------------
void Note::transposeDiatonic(int interval, bool keepAlterations, bool useDoubleAccidentals)
{
// compute note current absolute step
int alter1;
int alter2;
int tick = chord()->segment()->tick();
int key = !staff() ? Key::KEY_C : staff()->keymap()->key(tick).accidentalType();
int absStep1 = pitch2absStepByKey(pitch(), tpc1(), key, &alter1);
int absStep2 = pitch2absStepByKey(pitch()-transposition(), tpc2(), key, &alter2);
// get pitch and tcp corresponding to unaltered degree for this key
int newPitch = absStep2pitchByKey(absStep1 + interval, key);
int newTpc1 = step2tpcByKey((absStep1 + interval) % STEP_DELTA_OCTAVE, key);
int newTpc2 = step2tpcByKey((absStep2 + interval) % STEP_DELTA_OCTAVE, key);
// if required, transfer original degree alteration to new pitch and tpc
if (keepAlterations) {
newPitch += alter1;
newTpc1 += alter1 * TPC_DELTA_SEMITONE;
newTpc2 += alter2 * TPC_DELTA_SEMITONE;
}
// check results are in ranges
while (newPitch > 127)
newPitch -= PITCH_DELTA_OCTAVE;
while (newPitch < 0)
newPitch += PITCH_DELTA_OCTAVE;
while (newTpc1 > Tpc::MAX)
newTpc1 -= TPC_DELTA_ENHARMONIC;
while (newTpc1 < Tpc::MIN)
newTpc1 += TPC_DELTA_ENHARMONIC;
while (newTpc2 > Tpc::MAX)
newTpc2 -= TPC_DELTA_ENHARMONIC;
while (newTpc2 < Tpc::MIN)
newTpc2 += TPC_DELTA_ENHARMONIC;
// if required, reduce double alterations
if (!useDoubleAccidentals) {
if (newTpc1 >= Tpc::F_SS)
newTpc1 -= TPC_DELTA_ENHARMONIC;
if (newTpc1 <= Tpc::B_BB)
newTpc1 += TPC_DELTA_ENHARMONIC;
if (newTpc2 >= Tpc::F_SS)
newTpc2 -= TPC_DELTA_ENHARMONIC;
if (newTpc2 <= Tpc::B_BB)
newTpc2 += TPC_DELTA_ENHARMONIC;
2013-05-13 18:49:17 +02:00
}
// store new data
score()->undoChangePitch(this, newPitch, newTpc1, newTpc2);
2013-05-13 18:49:17 +02:00
}
}
2012-05-26 14:26:10 +02:00