LCOV - code coverage report
Current view: top level - src/project/internal - mdlmigrator.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 208 0
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) 2024 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 "mdlmigrator.h"
      24              : 
      25              : #include "engraving/dom/chord.h"
      26              : #include "engraving/dom/drumset.h"
      27              : #include "engraving/dom/masterscore.h"
      28              : #include "engraving/dom/note.h"
      29              : #include "engraving/dom/part.h"
      30              : #include "engraving/rw/xmlreader.h"
      31              : #include "io/file.h"
      32              : 
      33              : #include "log.h"
      34              : 
      35              : using namespace mu;
      36              : using namespace mu::project;
      37              : using namespace mu::engraving;
      38              : using namespace muse;
      39              : using namespace muse::io;
      40              : 
      41            0 : void MdlMigrator::remapPercussion()
      42              : {
      43            0 :     IF_ASSERT_FAILED(m_score) {
      44            0 :         return;
      45              :     }
      46              : 
      47            0 :     for (Part* part : m_score->parts()) {
      48            0 :         const TrackRange trackRange = part->trackRange();
      49            0 :         const InstrumentList& instruments = part->instruments();
      50              : 
      51              :         // First instrument in list is the "default instrument".
      52              :         // It's at tick -1 but we must start at tick 0.
      53            0 :         auto it = instruments.cbegin();
      54            0 :         for (Fraction startTick(0, 1), endTick; it != instruments.cend(); startTick = endTick) {
      55            0 :             Instrument* instr = it->second;
      56            0 :             ++it; // careful, iterator now points to next instrument in the list
      57            0 :             endTick = (it == instruments.cend())
      58            0 :                       ? m_score->endTick()
      59            0 :                       : Fraction::fromTicks(it->first);
      60              : 
      61            0 :             if (!instr || !instr->useDrumset() || !instr->musicXmlId().startsWith(u"mdl.")) {
      62            0 :                 continue;
      63              :             }
      64              : 
      65            0 :             RepitchFunc repitch;
      66            0 :             path_t drumsetPath = globalConfiguration()->appDataPath() + "templates/";
      67              : 
      68            0 :             if (!needToRemap(*instr, repitch, drumsetPath)) {
      69            0 :                 continue;
      70              :             }
      71              : 
      72            0 :             remapPitches(trackRange.startTrack, trackRange.endTrack, startTick, endTick, repitch);
      73              : 
      74            0 :             Drumset* drumset = instr->drumset();
      75              : 
      76            0 :             if (!loadDrumset(drumset, drumsetPath)) {
      77            0 :                 Drumset newDrumset;
      78            0 :                 newDrumset.clear();
      79            0 :                 for (int i = 0; i < DRUM_INSTRUMENTS; ++i) {
      80            0 :                     if (drumset->isValid(i)) {
      81            0 :                         newDrumset.setDrum(repitch(i), drumset->drum(i));
      82              :                     }
      83              :                 }
      84            0 :                 instr->setDrumset(&newDrumset);
      85              :                 // Note: The new drumset may have fewer drums than the old one.
      86              :                 // This happens if there were two pitches X and Y for which
      87              :                 // repitch(X) == repitch(Y), and the old drumset had drums on
      88              :                 // both X and Y. The winning drum is the higher of X and Y.
      89            0 :             }
      90            0 :         }
      91              :     }
      92              : }
      93              : 
      94            0 : bool MdlMigrator::loadDrumset(Drumset* drumset, path_t path)
      95              : {
      96            0 :     File file(path);
      97            0 :     if (!file.open(IODevice::ReadOnly)) {
      98            0 :         return false;
      99              :     }
     100            0 :     XmlReader xml(&file);
     101            0 :     drumset->clear();
     102            0 :     while (xml.readNextStartElement()) {
     103            0 :         if (xml.name() == "museScore") {
     104            0 :             bool ok = true;
     105            0 :             const String verString = xml.attribute("version");
     106            0 :             const int version = String(verString).remove(u".").toInt(&ok);
     107            0 :             IF_ASSERT_FAILED(ok && version <= Constants::MSC_VERSION) {
     108            0 :                 LOGW("Loading drumset from unrecognized MuseScore version <%s>", verString.toAscii().constData());
     109              :             }
     110            0 :             while (xml.readNextStartElement()) {
     111            0 :                 if (xml.name() == "Drum") {
     112            0 :                     drumset->loadDrum(xml);
     113              :                 } else {
     114            0 :                     xml.unknown();
     115              :                 }
     116              :             }
     117            0 :         }
     118              :     }
     119            0 :     file.close();
     120            0 :     return true;
     121            0 : }
     122              : 
     123            0 : void MdlMigrator::remapPitches(track_idx_t startTrack, track_idx_t endTrack, Fraction startTick, Fraction endTick,
     124              :                                const RepitchFunc& repitch)
     125              : {
     126            0 :     const auto repitchChord = [repitch] (Chord* chord) {
     127            0 :         for (Note* note : chord->notes()) {
     128            0 :             note->setPitch(repitch(note->pitch()));
     129            0 :             note->setTpcFromPitch(Prefer::SHARPS); // required for playback
     130              :         }
     131            0 :     };
     132              : 
     133            0 :     for (
     134            0 :         Segment* seg = m_score->tick2segment(startTick, true, SegmentType::ChordRest);
     135            0 :         seg && seg->tick() < endTick;
     136            0 :         seg = seg->next1(SegmentType::ChordRest)
     137              :         ) {
     138            0 :         for (track_idx_t track = startTrack; track < endTrack; ++track) {
     139            0 :             EngravingItem* el = seg->element(track);
     140            0 :             if (!el || !el->isChord()) {
     141            0 :                 continue;
     142              :             }
     143            0 :             Chord* ch = toChord(el);
     144            0 :             repitchChord(ch);
     145            0 :             for (Chord* gch : ch->graceNotes()) {
     146            0 :                 repitchChord(gch);
     147              :             }
     148              :         }
     149              :     }
     150            0 : }
     151              : 
     152            0 : bool MdlMigrator::needToRemap(const Instrument& instr, RepitchFunc& repitch, path_t& drumsetPath)
     153              : {
     154            0 :     const Drumset* drumset = instr.drumset();
     155            0 :     const InstrChannel* channel = instr.channel(0);
     156              : 
     157            0 :     if (!drumset || !channel || channel->synti() != u"Zerberus") {
     158            0 :         return false;
     159              :     }
     160              : 
     161            0 :     const int program = channel->program();
     162            0 :     const String musicXmlId = instr.musicXmlId();
     163              : 
     164            0 :     if (musicXmlId == u"mdl.drum.snare-drum" && (program == 5 || program == 6)) {
     165              :         // MDL Snare Line   (id="mdl-snareline")
     166              :         // MDL Snare Line A (id="mdl-snareline-a")
     167              :         // MDL Snare        (id="mdl-snaresolo")
     168              :         // MDL Snare A      (id="mdl-snaresolo-a")
     169            0 :         drumsetPath += "Marching_Snare_Drums.drm";
     170            0 :         repitch = repitchMdlSnares;
     171            0 :     } else if (musicXmlId == u"mdl.drum.tenor-drum" && (program == 7 || program == 8)) {
     172              :         // MDL Tenor Line   (id="mdl-tenorline")
     173              :         // MDL Tenors       (id="mdl-tenorsolo")
     174            0 :         drumsetPath += "Marching_Tenors.drm";
     175            0 :         repitch = repitchMdlTenors;
     176            0 :     } else if (musicXmlId == u"mdl.drum.bass-drum" && program == 0) {
     177            0 :         drumsetPath += "Marching_Bass_Drums.drm";
     178            0 :         if (drumset->isValid(61)) {
     179              :             // MDL Bass Line (10)   (id="mdl-bassline-10")
     180            0 :             repitch = repitchMdlBassline10;
     181              :         } else {
     182              :             // MDL Bass Line (5)    (id="mdl-bassline-5")
     183            0 :             repitch = repitchMdlBassline5;
     184              :         }
     185            0 :     } else if (musicXmlId == u"mdl.metal.cymbal.crash" && program == 1) {
     186              :         // MDL Cymbal Line  (id="mdl-cymballine")
     187            0 :         drumsetPath += "Marching_Cymbals.drm";
     188            0 :         repitch = repitchMdlCymballine;
     189              :     } else {
     190            0 :         return false;
     191              :     }
     192            0 :     return true;
     193            0 : }
     194              : 
     195            0 : int MdlMigrator::repitchMdlSnares(int pitch)
     196              : {
     197            0 :     switch (pitch) {
     198            0 :     case 23: return 55;
     199            0 :     case 27: return 25;
     200            0 :     case 29: return 92;
     201            0 :     case 30: return 91;
     202            0 :     case 31: return 89;
     203            0 :     case 32: return 58;
     204            0 :     case 33: return 71;
     205            0 :     case 36: return 76;
     206            0 :     case 38: return 40;
     207            0 :     case 39: return 38;
     208            0 :     case 40: return 36;
     209            0 :     case 60: return 50;
     210            0 :     case 61: return 51;
     211            0 :     case 62: return 56;
     212            0 :     case 63: return 53;
     213            0 :     case 64: return 49;
     214            0 :     case 65: return 57;
     215            0 :     case 67: return 60;
     216            0 :     case 68: return 60;
     217            0 :     case 72: return 65;
     218            0 :     case 73: return 67;
     219            0 :     case 74: return 59;
     220            0 :     case 76: return 55;
     221            0 :     case 77: return 24;
     222              :     }
     223            0 :     return pitch;
     224              : }
     225              : 
     226            0 : int MdlMigrator::repitchMdlTenors(int pitch)
     227              : {
     228            0 :     switch (pitch) {
     229            0 :     case 47: return 40;
     230            0 :     case 60: return 36;
     231            0 :     case 61: return 48;
     232            0 :     case 62: return 60;
     233            0 :     case 63: return 72;
     234            0 :     case 64: return 84;
     235            0 :     case 65: return 96;
     236            0 :     case 72: return 41;
     237            0 :     case 73: return 53;
     238            0 :     case 74: return 65;
     239            0 :     case 75: return 77;
     240            0 :     case 76: return 89;
     241            0 :     case 77: return 101;
     242            0 :     case 78: return 37;
     243            0 :     case 79: return 49;
     244            0 :     case 80: return 61;
     245            0 :     case 81: return 73;
     246            0 :     case 82: return 85;
     247            0 :     case 83: return 97;
     248            0 :     case 96: return 37;
     249            0 :     case 97: return 49;
     250            0 :     case 98: return 61;
     251            0 :     case 107: return 36;
     252              :     }
     253            0 :     return pitch;
     254              : }
     255              : 
     256            0 : int MdlMigrator::repitchMdlBassline10(int pitch)
     257              : {
     258            0 :     switch (pitch) {
     259            0 :     case 60: return 90;
     260            0 :     case 61: return 37;
     261            0 :     case 62: return 37;
     262            0 :     case 63: return 49;
     263            0 :     case 64: return 49;
     264            0 :     case 65: return 61;
     265            0 :     case 66: return 61;
     266            0 :     case 67: return 73;
     267            0 :     case 68: return 73;
     268            0 :     case 69: return 85;
     269            0 :     case 70: return 85;
     270            0 :     case 72: return 92;
     271            0 :     case 73: return 39;
     272            0 :     case 74: return 39;
     273            0 :     case 75: return 51;
     274            0 :     case 76: return 51;
     275            0 :     case 77: return 63;
     276            0 :     case 78: return 63;
     277            0 :     case 79: return 75;
     278            0 :     case 80: return 75;
     279            0 :     case 81: return 87;
     280            0 :     case 82: return 87;
     281              :     }
     282            0 :     return pitch;
     283              : }
     284              : 
     285            0 : int MdlMigrator::repitchMdlBassline5(int pitch)
     286              : {
     287            0 :     switch (pitch) {
     288            0 :     case 60: return 90;
     289            0 :     case 62: return 37;
     290            0 :     case 64: return 49;
     291            0 :     case 67: return 61;
     292            0 :     case 68: return 73;
     293            0 :     case 70: return 85;
     294            0 :     case 72: return 92;
     295            0 :     case 74: return 39;
     296            0 :     case 76: return 51;
     297            0 :     case 79: return 63;
     298            0 :     case 80: return 75;
     299            0 :     case 82: return 87;
     300              :     }
     301            0 :     return pitch;
     302              : }
     303              : 
     304            0 : int MdlMigrator::repitchMdlCymballine(int pitch)
     305              : {
     306            0 :     switch (pitch) {
     307            0 :     case 38: return 72;
     308            0 :     case 43: return 81;
     309            0 :     case 45: return 76;
     310            0 :     case 47: return 77;
     311            0 :     case 50: return 84;
     312            0 :     case 51: return 91;
     313            0 :     case 62: return 72;
     314            0 :     case 67: return 81;
     315            0 :     case 69: return 76;
     316            0 :     case 71: return 77;
     317            0 :     case 74: return 84;
     318            0 :     case 75: return 91;
     319            0 :     case 86: return 72;
     320            0 :     case 91: return 81;
     321            0 :     case 93: return 76;
     322            0 :     case 95: return 77;
     323            0 :     case 98: return 84;
     324            0 :     case 99: return 91;
     325              :     }
     326            0 :     return pitch;
     327              : }
        

Generated by: LCOV version 2.0-1