LCOV - code coverage report
Current view: top level - src/engraving/dom - key.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 84.3 % 140 118
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "key.h"
      24              : 
      25              : #include "accidental.h"
      26              : #include "part.h"
      27              : #include "pitchspelling.h"
      28              : #include "score.h"
      29              : #include "utils.h"
      30              : 
      31              : #include "log.h"
      32              : 
      33              : using namespace mu;
      34              : 
      35              : namespace mu::engraving {
      36              : // transposition table for microtonal accidentals
      37              : const SymId accTable[] = {
      38              :     SymId::noSym,
      39              :     // standard accidentals
      40              :     SymId::accidentalTripleFlat,
      41              :     SymId::accidentalDoubleFlat,
      42              :     SymId::accidentalFlat,
      43              :     SymId::accidentalNatural,
      44              :     SymId::accidentalSharp,
      45              :     SymId::accidentalDoubleSharp,
      46              :     SymId::accidentalTripleSharp,
      47              :     SymId::noSym,
      48              :     //  natural sharp
      49              :     SymId::accidentalNatural,
      50              :     SymId::accidentalNaturalSharp,
      51              :     SymId::accidentalSharp,
      52              :     SymId::noSym,
      53              :     //  natural flat
      54              :     SymId::accidentalDoubleFlat,
      55              :     SymId::accidentalNaturalFlat,
      56              :     SymId::accidentalNatural,
      57              :     SymId::noSym,
      58              :     //  natural sharp sharp
      59              :     SymId::accidentalSharp,
      60              :     SymId::accidentalSharpSharp,
      61              :     SymId::accidentalTripleSharp,
      62              :     SymId::noSym,
      63              :     // Gould quarter tone
      64              :     //  arrow down
      65              :     SymId::accidentalFiveQuarterTonesFlatArrowDown,
      66              :     SymId::accidentalThreeQuarterTonesFlatArrowDown,
      67              :     SymId::accidentalQuarterToneFlatNaturalArrowDown,
      68              :     SymId::accidentalQuarterToneSharpArrowDown,
      69              :     SymId::accidentalThreeQuarterTonesSharpArrowDown,
      70              :     SymId::accidentalFiveQuarterTonesFlatArrowDown,
      71              :     SymId::noSym,
      72              :     //  arrow up
      73              :     SymId::accidentalThreeQuarterTonesFlatArrowUp,
      74              :     SymId::accidentalQuarterToneFlatArrowUp,
      75              :     SymId::accidentalQuarterToneSharpNaturalArrowUp,
      76              :     SymId::accidentalThreeQuarterTonesSharpArrowUp,
      77              :     SymId::accidentalFiveQuarterTonesSharpArrowUp,
      78              :     SymId::noSym,
      79              :     // Stein-Zimmermann
      80              :     //  basic
      81              :     SymId::accidentalThreeQuarterTonesFlatZimmermann,
      82              :     SymId::accidentalQuarterToneFlatStein,
      83              :     SymId::accidentalQuarterToneSharpStein,
      84              :     SymId::accidentalThreeQuarterTonesSharpStein,
      85              :     SymId::noSym,
      86              :     //  narrow (as variant of above)
      87              :     SymId::accidentalNarrowReversedFlatAndFlat,
      88              :     SymId::accidentalNarrowReversedFlat,
      89              :     SymId::accidentalQuarterToneSharpStein,
      90              :     // Extended Helmholtz-Ellis (just intonation)
      91              :     //  one syntonic comma down arrow
      92              :     SymId::accidentalDoubleFlatOneArrowDown,
      93              :     SymId::accidentalFlatOneArrowDown,
      94              :     SymId::accidentalNaturalOneArrowDown,
      95              :     SymId::accidentalSharpOneArrowDown,
      96              :     SymId::accidentalDoubleSharpOneArrowDown,
      97              :     SymId::noSym,
      98              :     //   one syntonic comma up arrow
      99              :     SymId::accidentalDoubleFlatOneArrowUp,
     100              :     SymId::accidentalFlatOneArrowUp,
     101              :     SymId::accidentalNaturalOneArrowUp,
     102              :     SymId::accidentalSharpOneArrowUp,
     103              :     SymId::accidentalDoubleSharpOneArrowUp,
     104              :     SymId::noSym,
     105              :     //   two syntonic commas down
     106              :     SymId::accidentalDoubleFlatTwoArrowsDown,
     107              :     SymId::accidentalFlatTwoArrowsDown,
     108              :     SymId::accidentalNaturalTwoArrowsDown,
     109              :     SymId::accidentalSharpTwoArrowsDown,
     110              :     SymId::accidentalDoubleSharpTwoArrowsDown,
     111              :     SymId::noSym,
     112              :     //   two syntonic commas up
     113              :     SymId::accidentalDoubleFlatTwoArrowsUp,
     114              :     SymId::accidentalFlatTwoArrowsUp,
     115              :     SymId::accidentalNaturalTwoArrowsUp,
     116              :     SymId::accidentalSharpTwoArrowsUp,
     117              :     SymId::accidentalDoubleSharpTwoArrowsUp,
     118              :     SymId::noSym,
     119              :     //   three syntonic commas down
     120              :     SymId::accidentalDoubleFlatThreeArrowsDown,
     121              :     SymId::accidentalFlatThreeArrowsDown,
     122              :     SymId::accidentalNaturalThreeArrowsDown,
     123              :     SymId::accidentalSharpThreeArrowsDown,
     124              :     SymId::accidentalDoubleSharpThreeArrowsDown,
     125              :     SymId::noSym,
     126              :     //   three syntonic commas up
     127              :     SymId::accidentalDoubleFlatThreeArrowsUp,
     128              :     SymId::accidentalFlatThreeArrowsUp,
     129              :     SymId::accidentalNaturalThreeArrowsUp,
     130              :     SymId::accidentalSharpThreeArrowsUp,
     131              :     SymId::accidentalDoubleSharpThreeArrowsUp,
     132              :     SymId::noSym,
     133              :     //   equal tempered semitone
     134              :     SymId::accidentalDoubleFlatEqualTempered,
     135              :     SymId::accidentalFlatEqualTempered,
     136              :     SymId::accidentalNaturalEqualTempered,
     137              :     SymId::accidentalSharpEqualTempered,
     138              :     SymId::accidentalDoubleSharpEqualTempered,
     139              :     SymId::noSym
     140              : };
     141              : 
     142              : //---------------------------------------------------------
     143              : //   enforceLimits - ensure _key
     144              : //   is within acceptable limits (-7 .. +7).
     145              : //   see KeySig::layout()
     146              : //---------------------------------------------------------
     147              : 
     148      1791060 : void KeySigEvent::enforceLimits(bool transposing)
     149              : {
     150      1791060 :     if (m_key < Key::MIN) {
     151            0 :         m_key = Key::MIN;
     152            0 :         LOGD("key < -7");
     153      1791060 :     } else if (m_key > Key::MAX) {
     154            4 :         m_key = Key::MAX;
     155            4 :         LOGD("key > 7");
     156              :     }
     157              : 
     158      1791060 :     if (transposing) {
     159         1553 :         return;
     160              :     }
     161              : 
     162      1789507 :     if (m_concertKey < Key::MIN) {
     163            0 :         m_concertKey = Key::MIN;
     164            0 :         LOGD("key < -7");
     165      1789507 :     } else if (m_concertKey > Key::MAX) {
     166            2 :         m_concertKey = Key::MAX;
     167            2 :         LOGD("key > 7");
     168              :     }
     169              : }
     170              : 
     171              : //---------------------------------------------------------
     172              : //   print
     173              : //---------------------------------------------------------
     174              : 
     175            0 : void KeySigEvent::print() const
     176              : {
     177            0 :     LOGD("<KeySigEvent: ");
     178            0 :     if (!isValid()) {
     179            0 :         LOGD("invalid>");
     180              :     } else {
     181            0 :         if (isAtonal()) {
     182            0 :             LOGD("atonal>");
     183            0 :         } else if (custom()) {
     184            0 :             LOGD("custom>");
     185              :         } else {
     186            0 :             LOGD("accidental %d>", int(m_key));
     187              :         }
     188              :     }
     189            0 : }
     190              : 
     191              : //---------------------------------------------------------
     192              : //   setKey
     193              : //---------------------------------------------------------
     194              : 
     195         1553 : void KeySigEvent::setKey(Key v)
     196              : {
     197         1553 :     m_key = v;
     198         1553 :     enforceLimits(true);
     199         1553 : }
     200              : 
     201              : //---------------------------------------------------------
     202              : //   setConcertKey
     203              : //---------------------------------------------------------
     204              : 
     205      1789503 : void KeySigEvent::setConcertKey(Key v)
     206              : {
     207      1789503 :     m_key = v;
     208      1789503 :     m_concertKey = v;
     209      1789503 :     enforceLimits();
     210      1789503 : }
     211              : 
     212              : //---------------------------------------------------------
     213              : //   setCustom
     214              : //---------------------------------------------------------
     215              : 
     216           29 : void KeySigEvent::setCustom(bool val)
     217              : {
     218           29 :     m_custom = val;
     219           29 :     if (m_key == Key::INVALID) {
     220           21 :         m_concertKey = Key::C;
     221           21 :         m_key = Key::C;
     222              :     }
     223           29 : }
     224              : 
     225              : //---------------------------------------------------------
     226              : //   KeySigEvent::operator==
     227              : //---------------------------------------------------------
     228              : 
     229        78427 : bool KeySigEvent::operator==(const KeySigEvent& e) const
     230              : {
     231        78427 :     if (e.m_custom != m_custom || e.m_mode != m_mode || e.m_forInstrumentChange != m_forInstrumentChange) {
     232           62 :         return false;
     233              :     }
     234        78365 :     if (m_custom && !isAtonal()) {
     235           60 :         if (e.m_customKeyDefs.size() != m_customKeyDefs.size()) {
     236            0 :             return false;
     237              :         }
     238           68 :         for (size_t i = 0; i < m_customKeyDefs.size(); ++i) {
     239              :             // check note and sym, don't care xAlt and octAlt
     240           60 :             if (e.m_customKeyDefs[i].degree != m_customKeyDefs[i].degree || e.m_customKeyDefs[i].sym != m_customKeyDefs[i].sym) {
     241           52 :                 return false;
     242              :             }
     243              :             // TODO: position matters // does it?
     244              :         }
     245            8 :         return true;
     246              :     }
     247        78305 :     return e.m_concertKey == m_concertKey && e.m_key == m_key;
     248              : }
     249              : 
     250              : //---------------------------------------------------------
     251              : //   calculateInterval
     252              : //    Calculates the interval to move from one key to another
     253              : //---------------------------------------------------------
     254              : 
     255           25 : Interval calculateInterval(Key key1, Key key2)
     256              : {
     257           25 :     int chromatic = 7 * ((int)key2 - (int)key1);
     258           25 :     chromatic = chromatic % 12;
     259           25 :     if (chromatic < 0) {
     260            1 :         chromatic += 12;
     261              :     }
     262           25 :     return Interval(chromatic);
     263              : }
     264              : 
     265              : //---------------------------------------------------------
     266              : //   initFromSubtype
     267              : //    for backward compatibility
     268              : //---------------------------------------------------------
     269              : 
     270            4 : void KeySigEvent::initFromSubtype(int st)
     271              : {
     272              :     //anatoly-os: legacy code. I don't understand why it is so overcomplicated.
     273              :     //Did refactoring to avoid exception on MSVC, but left the same logic.
     274              :     struct {
     275              :         int _key : 4;
     276              :         int _naturalType : 4;
     277              :         unsigned _customType : 16;
     278              :         bool _custom : 1;
     279              :         bool _invalid : 1;
     280              :     } a;
     281              : 
     282            4 :     a._key         = (st & 0xf);
     283            4 :     a._naturalType = (st >> 4) & 0xf;
     284            4 :     a._customType  = (st >> 8) & 0xffff;
     285            4 :     a._custom      = (st >> 24) & 0x1;
     286            4 :     a._invalid     = (st >> 25) & 0x1;
     287              :     //end of legacy code
     288              : 
     289            4 :     m_key            = Key(a._key);
     290            4 :     m_concertKey     = m_key;
     291              : //      _customType     = a._customType;
     292            4 :     m_custom         = a._custom;
     293            4 :     if (a._invalid) {
     294            0 :         m_key = Key::INVALID;
     295              :     }
     296            4 :     enforceLimits();
     297            4 : }
     298              : 
     299              : //---------------------------------------------------------
     300              : //   accidentalVal
     301              : //---------------------------------------------------------
     302              : 
     303        56574 : AccidentalVal AccidentalState::accidentalVal(int line, bool& error) const
     304              : {
     305        56574 :     if (line < MIN_ACC_STATE || line >= MAX_ACC_STATE) {
     306           17 :         error = true;
     307           17 :         return AccidentalVal::NATURAL;
     308              :     }
     309        56557 :     return AccidentalVal((m_state[line] & 0x0f) + int(AccidentalVal::MIN));
     310              : }
     311              : 
     312              : //---------------------------------------------------------
     313              : //   init
     314              : //    preset lines list with accidentals for given key
     315              : //---------------------------------------------------------
     316              : 
     317              : static const int ACC_STATE_NATURAL = int(AccidentalVal::NATURAL) - int(AccidentalVal::MIN);
     318              : static const int ACC_STATE_FLAT = int(AccidentalVal::FLAT) - int(AccidentalVal::MIN);
     319              : static const int ACC_STATE_SHARP = int(AccidentalVal::SHARP) - int(AccidentalVal::MIN);
     320              : 
     321        53366 : void AccidentalState::init(Key key)
     322              : {
     323        53366 :     memset(m_state, ACC_STATE_NATURAL, MAX_ACC_STATE);
     324              :     // The numerical value of key tells us the number of sharps (or flats, if negative) in the key signature
     325        53366 :     if (key > 0 && key <= Key::MAX) {
     326        10676 :         for (int i = 0; i < int(key); ++i) {
     327              :             // First F#, then C#, then G#, etc.
     328         7568 :             int idx = tpc2step(Tpc::TPC_F_S + i);
     329        90638 :             for (int octave = 0; octave < (11 * 7); octave += 7) {
     330        83248 :                 int j = idx + octave;
     331        83248 :                 if (j >= MAX_ACC_STATE) {
     332          178 :                     break;
     333              :                 }
     334        83070 :                 m_state[j] = ACC_STATE_SHARP;
     335              :             }
     336              :         }
     337        50258 :     } else if (key < 0 && key >= Key::MIN) {
     338         6262 :         for (int i = 0; i > int(key); --i) {
     339              :             // First Bb, then Eb, then Ab, etc.
     340         4459 :             int idx = tpc2step(Tpc::TPC_B_B + i);
     341        50993 :             for (int octave = 0; octave < (11 * 7); octave += 7) {
     342        49049 :                 int j = idx + octave;
     343        49049 :                 if (j >= MAX_ACC_STATE) {
     344         2515 :                     break;
     345              :                 }
     346        46534 :                 m_state[j] = ACC_STATE_FLAT;
     347              :             }
     348              :         }
     349              :     }
     350        53366 : }
     351              : 
     352              : //---------------------------------------------------------
     353              : //   init
     354              : //---------------------------------------------------------
     355              : 
     356        50626 : void AccidentalState::init(const KeySigEvent& keySig)
     357              : {
     358        50626 :     init(keySig.key());
     359        50626 :     if (keySig.custom()) {
     360          202 :         for (const CustDef& d : keySig.customKeyDefs()) {
     361          100 :             SymId sym = keySig.symInKey(d.sym, d.degree);
     362          100 :             int degree = keySig.degInKey(d.degree);
     363          100 :             AccidentalVal a = sym2accidentalVal(sym);
     364         1176 :             for (int octave = 0; octave < (11 * 7); octave += 7) {
     365         1100 :                 int i = degree + octave;
     366         1100 :                 if (i >= MAX_ACC_STATE) {
     367           24 :                     break;
     368              :                 }
     369         1076 :                 m_state[i] = static_cast<uint8_t>(int(a) - int(AccidentalVal::MIN));
     370              :             }
     371              :         }
     372              :     }
     373        50626 :     m_forceRestateAccidental.fill(false);
     374        50626 : }
     375              : 
     376              : //---------------------------------------------------------
     377              : //   accidentalVal
     378              : //---------------------------------------------------------
     379              : 
     380          693 : AccidentalVal AccidentalState::accidentalVal(int line) const
     381              : {
     382          693 :     assert(line >= MIN_ACC_STATE && line < MAX_ACC_STATE);
     383          693 :     return AccidentalVal((m_state[line] & 0x0f) + int(AccidentalVal::MIN));
     384              : }
     385              : 
     386        52503 : bool AccidentalState::forceRestateAccidental(int line) const
     387              : {
     388        52503 :     assert(line >= MIN_ACC_STATE && line < MAX_ACC_STATE);
     389        52503 :     return m_forceRestateAccidental[line];
     390              : }
     391              : 
     392              : //---------------------------------------------------------
     393              : //   tieContext
     394              : //---------------------------------------------------------
     395              : 
     396        52517 : bool AccidentalState::tieContext(int line) const
     397              : {
     398        52517 :     assert(line >= MIN_ACC_STATE && line < MAX_ACC_STATE);
     399        52517 :     return m_state[line] & TIE_CONTEXT;
     400              : }
     401              : 
     402              : //---------------------------------------------------------
     403              : //   setAccidentalVal
     404              : //---------------------------------------------------------
     405              : 
     406         4437 : void AccidentalState::setAccidentalVal(int line, AccidentalVal val, bool tieContext)
     407              : {
     408         4437 :     assert(line >= MIN_ACC_STATE && line < MAX_ACC_STATE);
     409              :     // casts needed to work around a bug in Xcode 4.2 on Mac, see #25910
     410         4437 :     assert(int(val) >= int(AccidentalVal::MIN) && int(val) <= int(AccidentalVal::MAX));
     411         4437 :     m_state[line] = (int(val) - int(AccidentalVal::MIN)) | (tieContext ? TIE_CONTEXT : 0);
     412         4437 : }
     413              : 
     414        56393 : void AccidentalState::setForceRestateAccidental(int line, bool forceRestate)
     415              : {
     416        56393 :     assert(line >= MIN_ACC_STATE && line < MAX_ACC_STATE);
     417        56393 :     m_forceRestateAccidental[line] = forceRestate;
     418        56393 : }
     419              : 
     420              : //---------------------------------------------------------
     421              : //   degInKey
     422              : //    degree to "absolute degree"
     423              : //    first degree in F major is F: 0 -> 3, ...
     424              : //---------------------------------------------------------
     425              : 
     426          396 : int KeySigEvent::degInKey(int degree) const
     427              : {
     428          396 :     return (degree + ((static_cast<int>(key()) + 7) * 4)) % 7;
     429              : }
     430              : 
     431              : //---------------------------------------------------------
     432              : //   symInKey
     433              : //---------------------------------------------------------
     434              : 
     435          198 : SymId KeySigEvent::symInKey(SymId sym, int degree) const
     436              : {
     437          198 :     degree = degInKey(degree);
     438          198 :     int keyval = static_cast<int>(key());
     439          594 :     int accIndex = std::distance(std::begin(accTable), std::find(std::begin(accTable), std::end(accTable), sym));
     440              : 
     441              :     // non transposed key
     442          198 :     if (keyval == 0 || std::abs(keyval) > 7) {
     443          198 :         return sym;
     444              :     }
     445              : 
     446              :     // sym is not transposable (it is not in table)
     447            0 :     if (!(accIndex < std::end(accTable) - std::begin(accTable))) {
     448            0 :         return SymId::noSym;
     449              :     }
     450              : 
     451            0 :     for (int i = 1; i <= std::abs(keyval); ++i) {
     452            0 :         if ((degree * 2 + 2) % 7 == (keyval < 0 ? 8 - i : i) % 7) {
     453            0 :             accIndex += keyval < 0 ? -1 : 1;
     454              :         }
     455              :     }
     456            0 :     return accTable[accIndex];
     457              : }
     458              : }
        

Generated by: LCOV version 2.0-1