LCOV - code coverage report
Current view: top level - src/engraving/editing - editenharmonicspelling.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 27.0 % 196 53
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) 2026 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 "editenharmonicspelling.h"
      24              : 
      25              : #include <algorithm>
      26              : #include <vector>
      27              : 
      28              : #include "transpose.h"
      29              : 
      30              : #include "../dom/chord.h"
      31              : #include "../dom/key.h"
      32              : #include "../dom/note.h"
      33              : #include "../dom/part.h"
      34              : #include "../dom/pitchspelling.h"
      35              : #include "../dom/score.h"
      36              : #include "../dom/segment.h"
      37              : #include "../dom/select.h"
      38              : #include "../dom/staff.h"
      39              : 
      40              : using namespace mu::engraving;
      41              : 
      42              : // table of alternative spellings for one octave
      43              : // each entry is the TPC of the note
      44              : //    tab1 does not contain double sharps
      45              : //    tab2 does not contain double flats
      46              : 
      47              : static constexpr int tab1[24] = {
      48              :     14,  2,    // 60  C   Dbb
      49              :     21,  9,    // 61  C#  Db
      50              :     16,  4,    // 62  D   Ebb
      51              :     23, 11,    // 63  D#  Eb
      52              :     18,  6,    // 64  E   Fb
      53              :     13,  1,    // 65  F   Gbb
      54              :     20,  8,    // 66  F#  Gb
      55              :     15,  3,    // 67  G   Abb
      56              :     22, 10,    // 68  G#  Ab
      57              :     17,  5,    // 69  A   Bbb
      58              :     24, 12,    // 70  A#  Bb
      59              :     19,  7,    // 71  B   Cb
      60              : };
      61              : 
      62              : static constexpr int tab2[24] = {
      63              :     26, 14,    // 60  B#  C
      64              :     21,  9,    // 61  C#  Db
      65              :     28, 16,    // 62  C## D
      66              :     23, 11,    // 63  D#  Eb
      67              :     30, 18,    // 64  D## E
      68              :     25, 13,    // 65  E#  F
      69              :     20,  8,    // 66  F#  Gb
      70              :     27, 15,    // 67  F## G
      71              :     22, 10,    // 68  G#  Ab
      72              :     29, 17,    // 69  G## A
      73              :     24, 12,    // 70  A#  Bb
      74              :     31, 19,    // 71  A## B
      75              : };
      76              : 
      77              : static constexpr int intervalPenalty[13] = {
      78              :     0, 0, 0, 0, 0, 0, 1, 3, 1, 1, 1, 3, 3
      79              : };
      80              : 
      81              : static constexpr int enharmonicSpelling[15][34] = {
      82              :     {
      83              : //Ces f  c  g  d  a  e  b
      84              :         1, 1, 1, 1, 1, 1,  // bb
      85              :         0, 0, 0, 0, 0, 0, 0, // b
      86              :         1, 1, 1, 1, 1, 1, 1,
      87              :         1, 1, 1, 1, 1, 1, 1, // #
      88              :         1, 1, 1, 1, 1, 1, 1 // ##
      89              :     },
      90              :     {
      91              : //Ges f  c  g  d  a  e  b
      92              :         1, 1, 1, 1, 1, 1,  // bb
      93              :         1, 0, 0, 0, 0, 0, 0, // b
      94              :         0, 1, 1, 1, 1, 1, 1,
      95              :         1, 1, 1, 1, 1, 1, 1, // #
      96              :         1, 1, 1, 1, 1, 1, 1 // ##
      97              :     },
      98              :     {
      99              : //Des f  c  g  d  a  e  b
     100              :         1, 1, 1, 1, 1, 1,  // bb
     101              :         1, 1, 0, 0, 0, 0, 0, // b
     102              :         0, 0, 1, 1, 1, 1, 1,
     103              :         1, 1, 1, 1, 1, 1, 1, // #
     104              :         1, 1, 1, 1, 1, 1, 1 // ##
     105              :     },
     106              :     {
     107              : //As  f  c  g  d  a  e  b
     108              :         1, 1, 1, 1, 1, 1,  // bb
     109              :         1, 1, 0, 0, 0, 0, 0, // b
     110              :         0, 0, 0, 1, 1, 1, 1,
     111              :         0, 1, 1, 1, 1, 1, 1, // #
     112              :         1, 1, 1, 1, 1, 1, 1 // ##
     113              :     },
     114              :     {
     115              : //Es  f  c  g  d  a  e  b
     116              :         1, 1, 1, 1, 1, 1,  // bb
     117              :         1, 1, 0, 0, 0, 0, 0, // b
     118              :         0, 0, 0, 0, 1, 1, 1,
     119              :         0, 0, 1, 1, 1, 1, 1, // #
     120              :         1, 1, 1, 1, 1, 1, 1 // ##
     121              :     },
     122              :     {
     123              : //Bb  f  c  g  d  a  e  b
     124              :         1, 1, 1, 1, 1, 1,  // bb
     125              :         1, 1, 0, 0, 0, 0, 0, // b
     126              :         0, 0, 0, 0, 0, 1, 1,
     127              :         1, 0, 0, 1, 1, 1, 1, // #     // (ws) penalty for f#
     128              :         1, 1, 1, 1, 1, 1, 1 // ##
     129              :     },
     130              :     {
     131              : //F   f  c  g  d  a  e  b           // extra penalty for a# b
     132              :         1, 1, 1, 1, 1, 1,  // bb
     133              :         1, 1, 0, 0, 0, 0, 0, // b
     134              :         0, 0, 0, 0, 0, 0, 1,
     135              :         0, 0, 0, 0, 1, 1, 1, // #
     136              :         1, 1, 1, 1, 1, 1, 1 // ##
     137              :     },
     138              :     {
     139              : //C   f  c  g  d  a  e  b
     140              :         1, 1, 1, 1, 1, 1,  // bb
     141              :         1, 1, 0, 0, 0, 0, 0, // b
     142              :         0, 0, 0, 0, 0, 0, 0,
     143              :         0, 0, 0, 0, 0, 1, 1, // #
     144              :         1, 1, 1, 1, 1, 1, 1 // ##
     145              :     },
     146              :     {
     147              : //G   f  c  g  d  a  e  b
     148              :         1, 1, 1, 1, 1, 1,  // bb
     149              :         1, 1, 1, 0, 0, 0, 0, // b
     150              :         1, 0, 0, 0, 0, 0, 0,
     151              :         0, 0, 0, 0, 0, 1, 1, // #
     152              :         1, 1, 1, 1, 1, 1, 1 // ##
     153              :     },
     154              :     {
     155              : //D   f  c  g  d  a  e  b
     156              :         1, 1, 1, 1, 1, 1,  // bb
     157              :         1, 1, 1, 1, 0, 0, 0, // b
     158              :         1, 1, 0, 0, 0, 0, 0,
     159              :         0, 0, 0, 0, 0, 1, 1, // #
     160              :         1, 1, 1, 1, 1, 1, 1 // ##
     161              :     },
     162              :     {
     163              : //A   f  c  g  d  a  e  b
     164              :         1, 1, 1, 1, 1, 1,  // bb
     165              :         1, 1, 1, 1, 1, 0, 0, // b
     166              :         1, 1, 1, 0, 0, 0, 0,
     167              :         0, 0, 0, 0, 0, 1, 1, // #
     168              :         1, 1, 1, 1, 1, 1, 1 // ##
     169              :     },
     170              :     {
     171              : //E   f  c  g  d  a  e  b
     172              :         1, 1, 1, 1, 1, 1,  // bb
     173              :         1, 1, 1, 1, 1, 1, 0, // b
     174              :         1, 1, 1, 1, 0, 0, 0,
     175              :         0, 0, 0, 0, 0, 1, 1, // #
     176              :         0, 0, 1, 1, 1, 1, 1 // ##
     177              :     },
     178              :     {
     179              : //H   f  c  g  d  a  e  b
     180              :         1, 1, 1, 1, 1, 1,  // bb
     181              :         1, 1, 1, 1, 1, 1, 1, // b
     182              :         1, 1, 1, 1, 1, 0, 0,
     183              :         0, 0, 0, 0, 0, 1, 1, // #
     184              :         1, 1, 1, 1, 1, 1, 1 // ##
     185              :     },
     186              :     {
     187              : //Fis f  c  g  d  a  e  b
     188              :         1, 1, 1, 1, 1, 1,  // bb
     189              :         1, 1, 1, 1, 1, 1, 1, // b
     190              :         100, 1, 1, 1, 1, 1, 0,
     191              :         0, 0, 0, 0, 0, 0, 0, // #
     192              :         0, 1, 1, 1, 1, 1, 1 // ##
     193              :     },
     194              :     {
     195              : //Cis f  c  g  d  a  e  b
     196              :         1, 1, 1, 1, 1, 1,  // bb
     197              :         1, 1, 0, 0, 0, 0, 0, // b  //Fis
     198              :         100, 1, 1, 1, 1, 0, 0,
     199              :         0, 0, 0, 0, 0, 0, 0, // #
     200              :         0, 0, 1, 1, 1, 1, 1 // ##
     201              :     }
     202              : };
     203              : 
     204            0 : static constexpr int penalty(int lof1, int lof2, int k)
     205              : {
     206            0 :     IF_ASSERT_FAILED((k >= 0 && k < 15)) {
     207            0 :         return 0;
     208              :     }
     209            0 :     assert(lof1 >= 0 && lof1 < 34);
     210            0 :     assert(lof2 >= 0 && lof2 < 34);
     211            0 :     int penalty  = enharmonicSpelling[k][lof1] * 4 + enharmonicSpelling[k][lof2] * 4;
     212            0 :     int distance = lof2 > lof1 ? lof2 - lof1 : lof1 - lof2;
     213            0 :     if (distance > 12) {
     214            0 :         penalty += 3;
     215              :     } else {
     216            0 :         penalty += intervalPenalty[distance];
     217              :     }
     218            0 :     return penalty;
     219              : }
     220              : 
     221            0 : static int computeWindow(const std::vector<Note*>& notes, int start, int end)
     222              : {
     223            0 :     int p   = 10000;
     224            0 :     int idx = -1;
     225              :     int pitch[10];
     226              :     int key[10];
     227              : 
     228            0 :     int i = start;
     229            0 :     int k = 0;
     230            0 :     while (i < end) {
     231            0 :         pitch[k] = notes[i]->pitch() % 12;
     232            0 :         Fraction tick = notes[i]->chord()->tick();
     233            0 :         key[k]   = int(notes[i]->staff()->key(tick)) + 7;
     234            0 :         if (key[k] < 0 || key[k] > 14) {
     235            0 :             LOGD("illegal key at tick %d: %d, window %d-%d",
     236            0 :                  tick.ticks(), key[k] - 7, start, end);
     237            0 :             return 0;
     238              :             // abort();
     239              :         }
     240            0 :         ++k;
     241            0 :         ++i;
     242              :     }
     243              : 
     244            0 :     for (; k < 10; ++k) {
     245            0 :         pitch[k] = pitch[k - 1];
     246            0 :         key[k]   = key[k - 1];
     247              :     }
     248              : 
     249            0 :     for (i = 0; i < 512; ++i) {
     250            0 :         int pa    = 0;
     251            0 :         int pb    = 0;
     252            0 :         int l     = pitch[0] * 2 + (i & 1);
     253            0 :         assert(l >= 0 && l <= static_cast<int>(sizeof(tab1) / sizeof(*tab1)));
     254            0 :         int lof1a = tab1[l];
     255            0 :         int lof1b = tab2[l];
     256              : 
     257            0 :         for (k = 1; k < 10; ++k) {
     258            0 :             int l1 = pitch[k] * 2 + ((i & (1 << k)) >> k);
     259            0 :             assert(l1 >= 0 && l1 <= static_cast<int>(sizeof(tab1) / sizeof(*tab1)));
     260            0 :             int lof2a = tab1[l1];
     261            0 :             int lof2b = tab2[l1];
     262            0 :             pa += penalty(lof1a, lof2a, key[k]);
     263            0 :             pb += penalty(lof1b, lof2b, key[k]);
     264            0 :             lof1a = lof2a;
     265            0 :             lof1b = lof2b;
     266              :         }
     267            0 :         if (pa < pb) {
     268            0 :             if (pa < p) {
     269            0 :                 p   = pa;
     270            0 :                 idx = i;
     271              :             }
     272              :         } else {
     273            0 :             if (pb < p) {
     274            0 :                 p   = pb;
     275            0 :                 idx = i * -1;
     276              :             }
     277              :         }
     278              :     }
     279              : 
     280            0 :     return idx;
     281              : }
     282              : 
     283            0 : static void changeAllTpcs(Note* n, int tpc1)
     284              : {
     285            0 :     if (!n) {
     286            0 :         return;
     287              :     }
     288            0 :     Interval v;
     289            0 :     Fraction tick = n->chord() ? n->chord()->tick() : Fraction(-1, 1);
     290            0 :     if (n->part() && n->part()->instrument(tick)) {
     291            0 :         v = n->staff()->transpose(tick);
     292            0 :         v.flip();
     293              :     }
     294            0 :     int tpc2 = Transpose::transposeTpc(tpc1, v, true);
     295            0 :     n->undoChangeProperty(Pid::TPC1, tpc1);
     296            0 :     n->undoChangeProperty(Pid::TPC2, tpc2);
     297            0 :     for (Note* tied : n->tiedNotes()) {
     298            0 :         tied->undoChangeProperty(Pid::TPC1, tpc1);
     299            0 :         tied->undoChangeProperty(Pid::TPC2, tpc2);
     300            0 :     }
     301              : }
     302              : 
     303            4 : static void spellNotelist(std::vector<Note*>& notes)
     304              : {
     305              :     static constexpr int WINDOW = 9;
     306              : 
     307            4 :     int n = int(notes.size());
     308              : 
     309            4 :     int start = 0;
     310            4 :     while (start < n) {
     311            0 :         int end = start + WINDOW;
     312            0 :         if (end > n) {
     313            0 :             end = n;
     314              :         }
     315            0 :         int opt = computeWindow(notes, start, end);
     316              :         const int* tab;
     317            0 :         if (opt < 0) {
     318            0 :             tab = tab2;
     319            0 :             opt *= -1;
     320              :         } else {
     321            0 :             tab = tab1;
     322              :         }
     323              : 
     324            0 :         if (start == 0) {
     325            0 :             changeAllTpcs(notes[0], tab[(notes[0]->pitch() % 12) * 2 + (opt & 1)]);
     326            0 :             if (n > 1) {
     327            0 :                 changeAllTpcs(notes[1], tab[(notes[1]->pitch() % 12) * 2 + ((opt & 2) >> 1)]);
     328              :             }
     329            0 :             if (n > 2) {
     330            0 :                 changeAllTpcs(notes[2], tab[(notes[2]->pitch() % 12) * 2 + ((opt & 4) >> 2)]);
     331              :             }
     332              :         }
     333            0 :         if ((end - start) >= 6) {
     334            0 :             changeAllTpcs(notes[start + 3], tab[(notes[start + 3]->pitch() % 12) * 2 + ((opt & 8) >> 3)]);
     335            0 :             changeAllTpcs(notes[start + 4], tab[(notes[start + 4]->pitch() % 12) * 2 + ((opt & 16) >> 4)]);
     336            0 :             changeAllTpcs(notes[start + 5], tab[(notes[start + 5]->pitch() % 12) * 2 + ((opt & 32) >> 5)]);
     337              :         }
     338            0 :         if (end == n) {
     339            0 :             int n1 = end - start;
     340              :             int k;
     341            0 :             switch (n1 - 6) {
     342            0 :             case 3:
     343            0 :                 k = end - start - 3;
     344            0 :                 changeAllTpcs(notes[end - 3], tab[(notes[end - 3]->pitch() % 12) * 2 + ((opt & (1 << k)) >> k)]);
     345              :                 [[fallthrough]];
     346            0 :             case 2:
     347            0 :                 k = end - start - 2;
     348            0 :                 changeAllTpcs(notes[end - 2], tab[(notes[end - 2]->pitch() % 12) * 2 + ((opt & (1 << k)) >> k)]);
     349              :                 [[fallthrough]];
     350            0 :             case 1:
     351            0 :                 k = end - start - 1;
     352            0 :                 changeAllTpcs(notes[end - 1], tab[(notes[end - 1]->pitch() % 12) * 2 + ((opt & (1 << k)) >> k)]);
     353              :             }
     354            0 :             break;
     355              :         }
     356              :         // advance to next window
     357            0 :         start += 3;
     358              :     }
     359            4 : }
     360              : 
     361            4 : void EditEnharmonicSpelling::spell(Score* score)
     362              : {
     363            4 :     IF_ASSERT_FAILED(score) {
     364            0 :         return;
     365              :     }
     366              : 
     367            8 :     for (staff_idx_t i = 0; i < score->nstaves(); ++i) {
     368            4 :         std::vector<Note*> notes;
     369          196 :         for (Segment* s = score->firstSegment(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
     370          192 :             track_idx_t strack = i * VOICES;
     371          192 :             track_idx_t etrack = strack + VOICES;
     372          960 :             for (track_idx_t track = strack; track < etrack; ++track) {
     373          768 :                 EngravingItem* e = s->element(track);
     374          768 :                 if (!e || !e->isChord()) {
     375          768 :                     continue;
     376              :                 }
     377              : 
     378            0 :                 Chord* chord = toChord(e);
     379            0 :                 std::copy_if(chord->notes().begin(), chord->notes().end(), std::back_inserter(notes), [score](EngravingItem* chordElement) {
     380            0 :                     return score->selection().isNone() || chordElement->selected();
     381              :                 });
     382              : 
     383            0 :                 for (Chord* graceChord : chord->graceNotes()) {
     384            0 :                     std::copy_if(graceChord->notes().begin(), graceChord->notes().end(), std::back_inserter(notes),
     385            0 :                                  [score](EngravingItem* chordElement) {
     386            0 :                         return score->selection().isNone() || chordElement->selected();
     387              :                     });
     388              :                 }
     389              :             }
     390              :         }
     391              : 
     392            4 :         spellNotelist(notes);
     393            4 :     }
     394              : }
     395              : 
     396            0 : void EditEnharmonicSpelling::spellWithSharpsOrFlats(Score* score, Prefer prefer)
     397              : {
     398            0 :     IF_ASSERT_FAILED(score) {
     399            0 :         return;
     400              :     }
     401              : 
     402            0 :     std::vector<Note*> notes = score->selection().noteList();
     403            0 :     for (Note* note : notes) {
     404            0 :         Interval interval = note->part()->instrument(note->chord()->tick())->transpose();
     405            0 :         int tpc1 = TPC_INVALID;
     406            0 :         int tpc2 = TPC_INVALID;
     407            0 :         if (note->staff()->concertPitch() || interval.isZero()) {
     408              :             // Note: using Key::C always and ignoring actual key signature. Otherwise, "respell with flats" would still use sharps
     409              :             // sometimes if they're in the key, and vice versa, which seems contrary to the intent of the command.
     410            0 :             tpc1 = pitch2tpc(note->pitch(), Key::C, prefer);
     411            0 :             interval.flip();
     412            0 :             tpc2 = Transpose::transposeTpc(tpc1, interval, true);
     413              :         } else {
     414              :             // Spell the transposed pitch first, then convert to concert pitch
     415            0 :             int writtenPitch = note->pitch() - interval.chromatic;
     416            0 :             tpc2 = pitch2tpc(writtenPitch, Key::C, prefer);
     417            0 :             tpc1 = Transpose::transposeTpc(tpc2, interval, true);
     418              :         }
     419              : 
     420            0 :         note->undoChangeProperty(Pid::TPC1, tpc1);
     421            0 :         note->undoChangeProperty(Pid::TPC2, tpc2);
     422            0 :         for (Note* tiedNote : note->tiedNotes()) {
     423            0 :             tiedNote->undoChangeProperty(Pid::TPC1, tpc1);
     424            0 :             tiedNote->undoChangeProperty(Pid::TPC2, tpc2);
     425            0 :         }
     426              :     }
     427            0 : }
     428              : 
     429           10 : void EditEnharmonicSpelling::changeEnharmonicSpelling(Score* score, bool both)
     430              : {
     431           10 :     IF_ASSERT_FAILED(score) {
     432            0 :         return;
     433              :     }
     434              : 
     435              :     static constexpr int tpcTable[36] = {
     436              :         26, 14,  2,        // 60  B#   C   Dbb
     437              :         21, 21,  9,        // 61  C#   C#  Db
     438              :         28, 16,  4,        // 62  C##  D   Ebb
     439              :         23, 23, 11,        // 63  D#   D#  Eb
     440              :         30, 18,  6,        // 64  D##  E   Fb
     441              :         25, 13,  1,        // 65  E#   F   Gbb
     442              :         20, 20,  8,        // 66  F#   F#  Gb
     443              :         27, 15,  3,        // 67  F##  G   Abb
     444              :         22, 22, 10,        // 68  G#   G#  Ab
     445              :         29, 17,  5,        // 69  G##  A   Bbb
     446              :         24, 24, 12,        // 70  A#   A#  Bb
     447              :         31, 19,  7,        // 71  A##  B   Cb
     448              :     };
     449              : 
     450           10 :     std::vector<Note*> notes = score->selection().uniqueNotes();
     451          140 :     for (Note* note : notes) {
     452          130 :         Staff* staff = note->staff();
     453          130 :         if (note->originPart()->instrument(note->tick())->useDrumset()) {
     454            0 :             continue;
     455              :         }
     456              : 
     457          130 :         if (staff->isTabStaff(note->tick())) {
     458            0 :             int string = note->line() + (both ? 1 : -1);
     459            0 :             int fret = note->originPart()->stringData(note->tick(), staff->idx())->fret(note->pitch(), string, staff);
     460            0 :             if (fret != -1) {
     461            0 :                 note->undoChangeProperty(Pid::FRET, fret);
     462            0 :                 note->undoChangeProperty(Pid::STRING, string);
     463              :             }
     464            0 :             continue;
     465            0 :         }
     466              : 
     467          130 :         int tpc = note->tpc();
     468         2246 :         for (int i = 0; i < 36; ++i) {
     469         2246 :             if (tpcTable[i] != tpc) {
     470         2116 :                 continue;
     471              :             }
     472              : 
     473          130 :             if ((i % 3) < 2) {
     474           78 :                 tpc = tpcTable[i] == tpcTable[i + 1] ? tpcTable[i + 2] : tpcTable[i + 1];
     475              :             } else {
     476           52 :                 tpc = tpcTable[i - 2];
     477              :             }
     478          130 :             break;
     479              :         }
     480              : 
     481          130 :         note->undoSetTpc(tpc);
     482          130 :         if (both || staff->part()->instrument(note->chord()->tick())->transpose().isZero()) {
     483           65 :             int transposedTpc = note->transposeTpc(tpc);
     484           65 :             if (note->concertPitch()) {
     485            0 :                 note->undoChangeProperty(Pid::TPC2, transposedTpc);
     486              :             } else {
     487           65 :                 note->undoChangeProperty(Pid::TPC1, transposedTpc);
     488              :             }
     489              :         }
     490              :     }
     491           10 : }
     492              : 
     493           16 : int EditEnharmonicSpelling::bestEnharmonicFit(const std::vector<int> tpcs, Key key)
     494              : {
     495           16 :     int keyIndex = int(key) - int(Key::MIN);
     496           16 :     if (keyIndex < 0 || keyIndex >= int(Key::NUM_OF)) {
     497            0 :         return tpcs.front();
     498              :     }
     499              : 
     500              :     // Highest penalty in enharmonicSpelling available (100) + 1
     501           16 :     int bestPenalty = 101;
     502           16 :     int closestTpc = Tpc::TPC_INVALID;
     503              : 
     504           38 :     for (int tpc : tpcs) {
     505           22 :         if (tpc == Tpc::TPC_INVALID) {
     506            0 :             continue;
     507              :         }
     508              : 
     509           22 :         int lof = tpc - Tpc::TPC_MIN;
     510           22 :         if (lof < 0 || lof >= 34) {
     511            0 :             continue;
     512              :         }
     513              : 
     514           22 :         int penalty = enharmonicSpelling[keyIndex][lof];
     515              : 
     516           22 :         if (penalty < bestPenalty) {
     517           20 :             bestPenalty = penalty;
     518           20 :             closestTpc = tpc;
     519              :         }
     520              :     }
     521              : 
     522           16 :     return closestTpc;
     523              : }
        

Generated by: LCOV version 2.0-1