LCOV - code coverage report
Current view: top level - src/importexport/tabledit/internal - voiceallocator.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 79.5 % 78 62
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) 2025 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 "importtef.h"
      24              : #include "note.h"
      25              : #include "voiceallocator.h"
      26              : #include "engraving/dom/mscore.h"
      27              : 
      28              : namespace mu::iex::tabledit {
      29          440 : bool VoiceAllocator::canAddTefNoteToVoice(const TefNote* const note, const int voice)
      30              : {
      31              :     // is there room after the previous note ?
      32          440 :     if (stopPosition(voice) <= note->position) {
      33              :         LOGN("add string %d fret %d to voice %d", note->string, note->fret, voice);
      34          382 :         return true;
      35              :     }
      36              :     // can the note go into a chord ?
      37           58 :     const auto notePlaying = notesPlaying.at(voice);
      38           58 :     if (notePlaying
      39           58 :         && !notePlaying->rest
      40           56 :         && !note->rest
      41           56 :         && notePlaying->position == note->position
      42           45 :         && notePlaying->duration == note->duration) {
      43              :         LOGN("add string %d fret %d to voice %d as chord", note->string, note->fret, voice);
      44           39 :         return true;
      45              :     }
      46           19 :     return false;
      47              : }
      48              : 
      49          421 : int VoiceAllocator::findFirstPossibleVoice(const TefNote* const note, const std::array<int, 3> voices)
      50              : {
      51          440 :     for (const auto v : voices) {
      52          440 :         if (canAddTefNoteToVoice(note, v)) {
      53          421 :             return v;
      54              :         }
      55              :     }
      56            0 :     return -1;
      57              : }
      58              : 
      59          440 : int VoiceAllocator::stopPosition(const size_t voice)
      60              : {
      61          440 :     if (mu::engraving::VOICES <= voice) {
      62            0 :         LOGD("incorrect voice %zu", voice);
      63            0 :         return -1;
      64              :     }
      65              : 
      66          440 :     const auto note = notesPlaying.at(voice);
      67          440 :     if (note) {
      68          353 :         return note->position + durationToInt(note->duration);
      69              :     }
      70           87 :     return 0;
      71              : }
      72              : 
      73          421 : void VoiceAllocator::appendNoteToVoice(const TefNote* const note, int voice)
      74              : {
      75              :     LOGN("position %d string %d fret %d voice %d", note->position, note->string, note->fret, voice);
      76          421 :     const auto nChords { voiceContents[voice].size() };
      77              :     LOGN("voice %d nChords %zu", voice, nChords);
      78          421 :     if (nChords == 0) {
      79              :         LOGN("create first chord");
      80           87 :         std::vector<const TefNote*> chord;
      81           87 :         chord.push_back(note);
      82           87 :         voiceContents[voice].push_back(chord);
      83           87 :     } else {
      84          334 :         const auto position { voiceContents[voice].at(nChords - 1).at(0)->position };
      85              :         LOGN("chord %zu position %d", nChords - 1, position);
      86          334 :         if (position == note->position) {
      87              :             LOGN("add to last chord");
      88           39 :             voiceContents[voice].at(nChords - 1).push_back(note);
      89              :         } else {
      90              :             LOGN("create next chord at position %d", note->position);
      91          295 :             std::vector<const TefNote*> chord;
      92          295 :             chord.push_back(note);
      93          295 :             voiceContents[voice].push_back(chord);
      94          295 :         }
      95              :     }
      96              :     LOGN("done");
      97          421 : }
      98              : 
      99              : // debug: dump voices
     100              : 
     101            0 : void VoiceAllocator::dump()
     102              : {
     103            0 :     for (size_t i = 0; i < mu::engraving::VOICES; ++i) {
     104              :         LOGN("- voice %zu", i);
     105            0 :         for (size_t j = 0; j < voiceContents.at(i).size(); ++j) {
     106              :             LOGN("  - chord %zu", j);
     107            0 :             for (const auto note : voiceContents.at(i).at(j)) {
     108              :                 LOGN("    - position %d string %d fret %d", note->position, note->string, note->fret);
     109              :             }
     110              :         }
     111              :     }
     112            0 : }
     113              : 
     114          421 : void VoiceAllocator::allocateVoice(const TefNote* const note, int voice)
     115              : {
     116          421 :     if (voice >= 0) {
     117              :         // do actual allocation
     118              :         // note chord info is lost
     119          421 :         if (allocations.count(note) == 0) {
     120          421 :             allocations[note] = voice;
     121          421 :             notesPlaying[voice] = note;
     122          421 :             appendNoteToVoice(note, voice);
     123              :         } else {
     124            0 :             LOGD("duplicate note allocation");
     125              :         }
     126              :     } else {
     127            0 :         LOGD("cannot add string %d fret %d to voice %d", note->string, note->fret, voice);
     128              :     }
     129          421 : }
     130              : 
     131          397 : void VoiceAllocator::addColumn(const std::vector<const TefNote*>& column)
     132              : {
     133          397 :     if (column.empty()) {
     134           61 :         return;
     135              :     }
     136              : 
     137              :     // first add the highest note to voice 0
     138          336 :     addNote(column.at(0), true);
     139          336 :     if (column.size() >= 2) {
     140              :         // then add the lowest note to voice 1
     141           49 :         addNote(column.at(column.size() - 1), false);
     142              :         // finally add the remaining notes where possible
     143           85 :         for (unsigned int i = 1; i < column.size() - 1; ++i) {
     144           36 :             addNote(column.at(i), true);
     145              :         }
     146              :     }
     147              : }
     148              : 
     149          421 : void VoiceAllocator::addNote(const TefNote* const note, const bool preferVoice0)
     150              : {
     151          421 :     int voice { -1 };
     152              :     LOGN("note position %d voice %d", note->position, static_cast<int>(note->voice));
     153          421 :     if (note->voice == Voice::UPPER) {
     154           18 :         voice = findFirstPossibleVoice(note, { 0, 2, 3 });
     155          403 :     } else if (note->voice == Voice::LOWER) {
     156           15 :         voice = findFirstPossibleVoice(note, { 1, 2, 3 });
     157              :     } else {
     158          388 :         if (preferVoice0) {
     159          346 :             voice = findFirstPossibleVoice(note, { 0, 1, 2 /* TODO , 3 */ });
     160              :         } else {
     161           42 :             voice = findFirstPossibleVoice(note, { 1, 0, 2 /* TODO , 3 */ });
     162              :         }
     163              :     }
     164          421 :     allocateVoice(note, voice);
     165          421 : }
     166              : 
     167            0 : int VoiceAllocator::voice(const TefNote* const note)
     168              : {
     169            0 :     int res { -1 }; // TODO -1 ?
     170            0 :     if (allocations.count(note) > 0) {
     171            0 :         res = allocations[note];
     172              :     } else {
     173            0 :         LOGD("no voice allocated for note %p", note);
     174              :     }
     175              : 
     176              :     LOGN("note %p voice %d res %d", note, static_cast<int>(note->voice), res);
     177            0 :     return res;
     178              : }
     179              : } // namespace mu::iex::tabledit
        

Generated by: LCOV version 2.0-1