LCOV - code coverage report
Current view: top level - src/notation/internal - instrumentsrepository.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 188 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) 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              : #include "instrumentsrepository.h"
      23              : 
      24              : #include "global/serialization/json.h"
      25              : 
      26              : #include "engraving/dom/instrtemplate.h"
      27              : #include "engraving/types/types.h"
      28              : 
      29              : #include "io/path.h"
      30              : #include "mpe/playbacksetupdata.h"
      31              : 
      32              : #include "log.h"
      33              : #include "translation.h"
      34              : 
      35              : using namespace muse;
      36              : using namespace muse::io;
      37              : using namespace mu::notation;
      38              : 
      39            0 : static InstrumentGroup* createAndAddOtherGroup()
      40              : {
      41            0 :     InstrumentGroup* otherGroup = new InstrumentGroup();
      42            0 :     otherGroup->id = "other_group";
      43            0 :     otherGroup->name = mtrc("instruments", "Other");
      44            0 :     mu::engraving::instrumentGroups.push_back(otherGroup);
      45              : 
      46            0 :     return otherGroup;
      47              : }
      48              : 
      49            0 : static mu::engraving::ClefType museSamplerClefTypeToEngravingClefType(musesampler::ClefType type)
      50              : {
      51            0 :     switch (type) {
      52            0 :     case musesampler::ClefType::Treble: return mu::engraving::ClefType::G;
      53            0 :     case musesampler::ClefType::Bass: return mu::engraving::ClefType::F;
      54            0 :     case musesampler::ClefType::Alto: return mu::engraving::ClefType::C3;
      55            0 :     case musesampler::ClefType::Tenor: return mu::engraving::ClefType::C4;
      56            0 :     case musesampler::ClefType::Percussion: return mu::engraving::ClefType::PERC;
      57            0 :     case musesampler::ClefType::HigherOctaveTreble: return mu::engraving::ClefType::G8_VA;
      58            0 :     case musesampler::ClefType::LowerOctaveTreble: return mu::engraving::ClefType::G8_VB;
      59            0 :     case musesampler::ClefType::HigherOctaveBass: return mu::engraving::ClefType::F_8VA;
      60            0 :     case musesampler::ClefType::LowerOctaveBass: return mu::engraving::ClefType::F8_VB;
      61            0 :     case musesampler::ClefType::Baritone: return mu::engraving::ClefType::C5;
      62            0 :     case musesampler::ClefType::Mezzosoprano: return mu::engraving::ClefType::C2;
      63            0 :     case musesampler::ClefType::Soprano: return mu::engraving::ClefType::C1;
      64            0 :     case musesampler::ClefType::FrenchViolin: return mu::engraving::ClefType::G_1;
      65            0 :     case musesampler::ClefType::None: break;
      66              :     }
      67              : 
      68            0 :     return mu::engraving::ClefType::G;
      69              : }
      70              : 
      71            0 : static size_t staffCount(musesampler::StaffType type)
      72              : {
      73            0 :     switch (type) {
      74            0 :     case musesampler::StaffType::Standard: return 1;
      75            0 :     case musesampler::StaffType::Grand: return 2;
      76              :     }
      77              : 
      78            0 :     return 1;
      79              : }
      80              : 
      81            0 : void InstrumentsRepository::init()
      82              : {
      83            0 :     configuration()->userInstrumentsFolderChanged().onReceive(this, [this](const auto&) {
      84            0 :         load();
      85            0 :     });
      86              : 
      87            0 :     load();
      88            0 : }
      89              : 
      90            0 : const InstrumentTemplateList& InstrumentsRepository::instrumentTemplates() const
      91              : {
      92            0 :     return m_instrumentTemplateList;
      93              : }
      94              : 
      95            0 : const InstrumentTemplate& InstrumentsRepository::instrumentTemplate(const String& instrumentId) const
      96              : {
      97            0 :     auto it = m_instrumentTemplateMap.find(instrumentId);
      98            0 :     if (it == m_instrumentTemplateMap.end()) {
      99            0 :         static const InstrumentTemplate dummy;
     100            0 :         return dummy;
     101              :     }
     102              : 
     103            0 :     return *it->second;
     104              : }
     105              : 
     106            0 : const ScoreOrderList& InstrumentsRepository::orders() const
     107              : {
     108            0 :     return mu::engraving::instrumentOrders;
     109              : }
     110              : 
     111            0 : const ScoreOrder& InstrumentsRepository::order(const String& orderId) const
     112              : {
     113            0 :     const ScoreOrderList& orders = mu::engraving::instrumentOrders;
     114              : 
     115            0 :     auto it = std::find_if(orders.begin(), orders.end(), [orderId](const ScoreOrder& order) {
     116            0 :         return order.id == orderId;
     117              :     });
     118              : 
     119            0 :     if (it == orders.cend()) {
     120            0 :         static ScoreOrder dummy;
     121            0 :         return dummy;
     122              :     }
     123              : 
     124            0 :     return *it;
     125              : }
     126              : 
     127            0 : const InstrumentGenreList& InstrumentsRepository::genres() const
     128              : {
     129            0 :     return mu::engraving::instrumentGenres;
     130              : }
     131              : 
     132            0 : const InstrumentGroupList& InstrumentsRepository::groups() const
     133              : {
     134            0 :     return mu::engraving::instrumentGroups;
     135              : }
     136              : 
     137            0 : const InstrumentStringTuningsMap& InstrumentsRepository::stringTuningsPresets() const
     138              : {
     139            0 :     return m_stringTuningsPresets;
     140              : }
     141              : 
     142            0 : void InstrumentsRepository::load()
     143              : {
     144            0 :     TRACEFUNC;
     145              : 
     146            0 :     m_instrumentTemplateList.clear();
     147            0 :     m_instrumentTemplateMap.clear();
     148              : 
     149            0 :     mu::engraving::clearInstrumentTemplates();
     150              : 
     151            0 :     const path_t instrumentsXmlPath = configuration()->instrumentsXmlPath();
     152            0 :     if (!mu::engraving::loadInstrumentTemplates(instrumentsXmlPath)) {
     153            0 :         LOGE() << "Could not load instruments from " << instrumentsXmlPath;
     154              :     }
     155              : 
     156            0 :     const path_t scoreOrdersXmlPath = configuration()->scoreOrdersXmlPath();
     157            0 :     if (!mu::engraving::loadInstrumentTemplates(scoreOrdersXmlPath)) {
     158            0 :         LOGE() << "Could not load score orders from " << scoreOrdersXmlPath;
     159              :     }
     160              : 
     161            0 :     for (const path_t& path : configuration()->userInstrumentsAndScoreOrdersPaths()) {
     162            0 :         if (!mu::engraving::loadInstrumentTemplates(path)) {
     163            0 :             LOGE() << "Could not load user instruments and score orders from " << path;
     164              :         }
     165            0 :     }
     166              : 
     167            0 :     InstrumentTemplateMap instrumentByMusicXmlId;
     168              : 
     169            0 :     for (const InstrumentGroup* group : mu::engraving::instrumentGroups) {
     170            0 :         for (const InstrumentTemplate* templ : group->instrumentTemplates) {
     171            0 :             if (templ->trackName.isEmpty() || templ->instrumentName.longName().empty()) {
     172            0 :                 continue;
     173              :             }
     174              : 
     175            0 :             m_instrumentTemplateList.push_back(templ);
     176            0 :             m_instrumentTemplateMap.insert_or_assign(templ->id, templ);
     177            0 :             instrumentByMusicXmlId.insert_or_assign(templ->musicXmlId, templ);
     178              :         }
     179              :     }
     180              : 
     181            0 :     loadMuseInstruments(instrumentByMusicXmlId);
     182              : 
     183            0 :     path_t stringTuningsPresetsPath = configuration()->stringTuningsPresetsPath();
     184            0 :     if (!loadStringTuningsPresets(stringTuningsPresetsPath)) {
     185            0 :         LOGE() << "Could not load string tunings presets from " << stringTuningsPresetsPath << "!";
     186              :     }
     187            0 : }
     188              : 
     189            0 : bool InstrumentsRepository::loadStringTuningsPresets(const path_t& path)
     190              : {
     191            0 :     TRACEFUNC;
     192              : 
     193            0 :     Ret ret = fileSystem()->exists(path);
     194            0 :     if (!ret) {
     195            0 :         LOGE() << ret.toString();
     196            0 :         return false;
     197              :     }
     198              : 
     199            0 :     RetVal<ByteArray> retVal = fileSystem()->readFile(path);
     200            0 :     if (!retVal.ret) {
     201            0 :         LOGE() << retVal.ret.toString();
     202            0 :         return false;
     203              :     }
     204              : 
     205            0 :     std::string err;
     206            0 :     JsonArray arr = JsonDocument::fromJson(retVal.val, &err).rootArray();
     207            0 :     if (!err.empty()) {
     208            0 :         LOGE() << "failed parse string tunings presets, err: " << err;
     209            0 :         return false;
     210              :     }
     211              : 
     212            0 :     for (size_t i = 0; i < arr.size(); ++i) {
     213            0 :         const JsonValue& presetInfoVal = arr.at(i);
     214            0 :         JsonObject presetInfoObj = presetInfoVal.toObject();
     215              : 
     216            0 :         std::vector<StringTuningsInfo> strings;
     217              : 
     218            0 :         JsonArray stringsArr = presetInfoObj.value("strings").toArray();
     219            0 :         for (size_t j = 0; j < stringsArr.size(); ++j) {
     220            0 :             StringTuningsInfo info;
     221              : 
     222            0 :             const JsonValue& stringVal = stringsArr.at(j);
     223            0 :             JsonObject stringObj = stringVal.toObject();
     224              : 
     225            0 :             info.number = stringObj.value("number").toInt();
     226              : 
     227            0 :             JsonArray presetsArr = stringObj.value("presets").toArray();
     228            0 :             for (size_t k = 0; k < presetsArr.size(); ++k) {
     229            0 :                 const JsonValue& presetVal = presetsArr.at(k);
     230            0 :                 JsonObject presetObj = presetVal.toObject();
     231              : 
     232            0 :                 StringTuningPreset preset;
     233            0 :                 preset.name = muse::trc("instruments/stringTunings", presetObj.value("name").toStdString().c_str());
     234              : 
     235            0 :                 JsonArray valuesArr = presetObj.value("value").toArray();
     236            0 :                 for (size_t l = 0; l < valuesArr.size(); ++l) {
     237            0 :                     const JsonValue& valueVal = valuesArr.at(l);
     238            0 :                     preset.value.push_back(valueVal.toInt());
     239            0 :                 }
     240              : 
     241            0 :                 if (info.number != preset.value.size()) {
     242            0 :                     LOGE() << "Invalid preset " << preset.name;
     243            0 :                     continue;
     244              :                 }
     245              : 
     246            0 :                 if (presetObj.contains("useFlats")) {
     247            0 :                     preset.useFlats = presetObj.value("useFlats").toBool();
     248              :                 }
     249              : 
     250            0 :                 info.presets.emplace_back(std::move(preset));
     251            0 :             }
     252              : 
     253            0 :             strings.emplace_back(std::move(info));
     254            0 :         }
     255              : 
     256            0 :         std::string id = presetInfoObj.contains("familyId") ? presetInfoObj.value("familyId").toStdString()
     257            0 :                          : presetInfoObj.value("instrumentId").toStdString();
     258              : 
     259            0 :         m_stringTuningsPresets.emplace(std::move(id), std::move(strings));
     260            0 :     }
     261              : 
     262            0 :     return true;
     263            0 : }
     264              : 
     265            0 : void InstrumentsRepository::loadMuseInstruments(const InstrumentTemplateMap& standardInstrumentByMusicXmlId)
     266              : {
     267            0 :     TRACEFUNC;
     268              : 
     269            0 :     if (!museSampler()) {
     270            0 :         return;
     271              :     }
     272              : 
     273            0 :     InstrumentGroup* otherGroup = nullptr;
     274            0 :     std::vector<musesampler::Instrument> instruments = museSampler()->instruments();
     275              : 
     276            0 :     for (const musesampler::Instrument& instrument : instruments) {
     277            0 :         mpe::PlaybackSetupData sound = mpe::PlaybackSetupData::fromString(instrument.soundId);
     278            0 :         if (!sound.isValid()) {
     279            0 :             continue;
     280              :         }
     281              : 
     282            0 :         if (sound.isKnownSound()) {
     283            0 :             continue;
     284              :         }
     285              : 
     286            0 :         InstrumentTemplate* templ = new InstrumentTemplate();
     287            0 :         templ->id = instrument.id;
     288            0 :         templ->soundId = instrument.soundId;
     289            0 :         templ->musicXmlId = instrument.musicXmlId;
     290            0 :         templ->trackName = instrument.name;
     291            0 :         templ->instrumentName.setLongName(instrument.name);
     292            0 :         templ->instrumentName.setShortName(instrument.abbreviation);
     293            0 :         templ->staffCount = staffCount(instrument.staffType);
     294            0 :         mu::engraving::ClefType clefType = museSamplerClefTypeToEngravingClefType(instrument.clefType);
     295            0 :         templ->clefTypes[0].concertClef = clefType;
     296            0 :         templ->clefTypes[0].transposingClef = clefType;
     297              : 
     298            0 :         if (instrument.staffType == musesampler::StaffType::Grand) {
     299            0 :             templ->bracketSpan[0] = static_cast<int>(templ->staffCount);
     300            0 :             templ->barlineSpan[0] = true;
     301              : 
     302            0 :             for (size_t i = 0; i < templ->staffCount; ++i) {
     303            0 :                 templ->bracket[i] = mu::engraving::BracketType::BRACE;
     304              :             }
     305              : 
     306            0 :             templ->clefTypes[1].concertClef = mu::engraving::ClefType::F;
     307            0 :             templ->clefTypes[1].transposingClef = mu::engraving::ClefType::F;
     308              :         }
     309              : 
     310            0 :         for (int i = 0; i < MAX_STAVES; ++i) {
     311            0 :             templ->staffLines[i] = static_cast<int>(instrument.staffLines);
     312              :         }
     313              : 
     314            0 :         if (!instrument.musicXmlId.empty()) {
     315            0 :             const InstrumentTemplate* standardTempl = muse::value(standardInstrumentByMusicXmlId, instrument.musicXmlId, nullptr);
     316            0 :             if (standardTempl) {
     317            0 :                 templ->family = standardTempl->family;
     318            0 :                 templ->groupId = standardTempl->groupId;
     319            0 :                 templ->genres = standardTempl->genres;
     320              :             }
     321              :         }
     322              : 
     323            0 :         if (templ->groupId.empty()) {
     324            0 :             if (!otherGroup) {
     325            0 :                 otherGroup = createAndAddOtherGroup();
     326              :             }
     327            0 :             templ->groupId = otherGroup->id;
     328              :         }
     329              : 
     330            0 :         if (templ->genres.empty()) {
     331            0 :             const InstrumentGenre* commonGenre = mu::engraving::searchInstrumentGenre(COMMON_GENRE_ID);
     332            0 :             if (commonGenre) {
     333            0 :                 templ->genres.push_back(commonGenre);
     334              :             }
     335              :         }
     336              : 
     337            0 :         m_instrumentTemplateList.push_back(templ);
     338            0 :         m_instrumentTemplateMap.insert_or_assign(templ->id, templ);
     339              : 
     340            0 :         mu::engraving::addTemplateToGroup(templ, templ->groupId);
     341            0 :     }
     342            0 : }
        

Generated by: LCOV version 2.0-1