LCOV - code coverage report
Current view: top level - muse/framework/musesampler/internal - musesamplerresolver.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 193 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-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 "musesamplerresolver.h"
      24              : 
      25              : #include "musesamplerwrapper.h"
      26              : 
      27              : #include "global/io/fileinfo.h"
      28              : #include "global/types/version.h"
      29              : #include "global/serialization/json.h"
      30              : 
      31              : #include "log.h"
      32              : 
      33              : using namespace muse;
      34              : using namespace muse::async;
      35              : using namespace muse::audio;
      36              : using namespace muse::audio::synth;
      37              : using namespace muse::musesampler;
      38              : 
      39              : static const std::unordered_map<String, ClefType> CLEF_NAME_LOWER_CASE_TO_TYPE {
      40              :     { u"none", ClefType::None },
      41              :     { u"treble", ClefType::Treble },
      42              :     { u"bass", ClefType::Bass },
      43              :     { u"alto", ClefType::Alto },
      44              :     { u"tenor", ClefType::Tenor },
      45              :     { u"percussion", ClefType::Percussion },
      46              :     { u"higheroctavetreble", ClefType::HigherOctaveTreble },
      47              :     { u"loweroctavetreble", ClefType::LowerOctaveTreble },
      48              :     { u"higheroctavebass", ClefType::HigherOctaveBass },
      49              :     { u"loweroctavebass", ClefType::LowerOctaveBass },
      50              :     { u"baritone", ClefType::Baritone },
      51              :     { u"mezzosoprano", ClefType::Mezzosoprano },
      52              :     { u"soprano", ClefType::Soprano },
      53              :     { u"frenchviolin", ClefType::FrenchViolin },
      54              : };
      55              : 
      56              : static const std::unordered_map<String, StaffType> STAFF_NAME_LOWER_CASE_TO_TYPE {
      57              :     { u"standard", StaffType::Standard },
      58              :     { u"grand", StaffType::Grand },
      59              : };
      60              : 
      61            0 : InstrumentInfo findInstrument(MuseSamplerLibHandlerPtr libHandler, const AudioResourceMeta& resourceMeta)
      62              : {
      63            0 :     if (!libHandler) {
      64            0 :         return InstrumentInfo();
      65              :     }
      66              : 
      67            0 :     auto instrumentList = libHandler->getInstrumentList();
      68              : 
      69            0 :     while (auto instrument = libHandler->getNextInstrument(instrumentList)) {
      70            0 :         const int instrumentId = libHandler->getInstrumentId(instrument);
      71              : 
      72            0 :         if (resourceMeta.attributeVal(u"museUID") == String::number(instrumentId)) {
      73            0 :             const bool isOnline = libHandler->isOnlineInstrument(instrument);
      74            0 :             return { instrumentId, instrument, isOnline };
      75              :         }
      76            0 :     }
      77              : 
      78            0 :     return InstrumentInfo();
      79              : }
      80              : 
      81            0 : void MuseSamplerResolver::init()
      82              : {
      83            0 :     TRACEFUNC;
      84              : 
      85            0 :     const io::path_t libraryPath = configuration()->libraryPath();
      86            0 :     if (io::isAbsolute(libraryPath) && !io::FileInfo::exists(libraryPath)) {
      87            0 :         LOGI() << "MuseSampler library not found: " << libraryPath;
      88            0 :         return;
      89              :     }
      90              : 
      91            0 :     m_libHandler = std::make_shared<MuseSamplerLibHandler>();
      92            0 :     if (!m_libHandler->loadLib(libraryPath)) {
      93            0 :         LOGE() << "Unable to load MuseSampler library: " << libraryPath;
      94            0 :         m_libHandler.reset();
      95            0 :         return;
      96              :     }
      97              : 
      98            0 :     if (!m_libHandler->loadApi(configuration()->minSupportedVersion(), configuration()->useLegacyAudition())) {
      99            0 :         m_samplerVersion = m_libHandler->version();
     100            0 :         m_samplerBuildNumber = m_libHandler->buildNumber();
     101            0 :         m_libHandler.reset();
     102            0 :         LOGE() << "Incompatible MuseSampler library: " << libraryPath << ", version: " << m_samplerVersion.toString();
     103            0 :         return;
     104              :     }
     105              : 
     106            0 :     m_samplerVersion = m_libHandler->version();
     107            0 :     m_samplerBuildNumber = m_libHandler->buildNumber();
     108              : 
     109            0 :     if (!m_libHandler->init()) {
     110            0 :         LOGE() << "Could not init MuseSampler: " << libraryPath << ", version: " << m_samplerVersion.toString();
     111            0 :         m_libHandler.reset();
     112            0 :         m_initFailed = true;
     113            0 :         return;
     114              :     }
     115              : 
     116            0 :     m_libHandler->setLoggingCallback([](ms_ErrorLevel level, const char* msg) {
     117            0 :         switch (level) {
     118            0 :             case ms_ErrorLevel_Debug:
     119            0 :                 LOGD() << msg;
     120            0 :                 break;
     121            0 :             case ms_ErrorLevel_Info:
     122            0 :                 LOGI() << msg;
     123            0 :                 break;
     124            0 :             case ms_ErrorLevel_Warning:
     125            0 :                 LOGW() << msg;
     126            0 :                 break;
     127            0 :             case ms_ErrorLevel_Error:
     128            0 :                 LOGE() << msg;
     129            0 :                 break;
     130              :         }
     131            0 :     });
     132              : 
     133            0 :     LOGI() << "MuseSampler successfully inited: " << libraryPath << ", version: " << m_samplerVersion.toString();
     134            0 : }
     135              : 
     136            0 : void MuseSamplerResolver::deinit()
     137              : {
     138            0 :     TRACEFUNC;
     139              : 
     140            0 :     if (m_libHandler) {
     141            0 :         m_libHandler->deinit();
     142              :     }
     143            0 : }
     144              : 
     145            0 : bool MuseSamplerResolver::reloadAllInstruments()
     146              : {
     147            0 :     if (!m_libHandler) {
     148            0 :         return false;
     149              :     }
     150              : 
     151            0 :     return m_libHandler->reloadAllInstruments() == ms_Result_OK;
     152              : }
     153              : 
     154            0 : int MuseSamplerResolver::buildNumber() const
     155              : {
     156            0 :     return m_samplerBuildNumber;
     157              : }
     158              : 
     159            0 : ISynthesizerPtr MuseSamplerResolver::resolveSynth(const TrackId /*trackId*/, const AudioInputParams& params,
     160              :                                                   const audio::OutputSpec&) const
     161              : {
     162            0 :     const InstrumentInfo instrument = findInstrument(m_libHandler, params.resourceMeta);
     163            0 :     if (instrument.isValid()) {
     164            0 :         return std::make_shared<MuseSamplerWrapper>(m_libHandler, instrument, params);
     165              :     }
     166              : 
     167            0 :     return nullptr;
     168              : }
     169              : 
     170            0 : bool MuseSamplerResolver::hasCompatibleResources(const PlaybackSetupData& setup) const
     171              : {
     172              :     UNUSED(setup);
     173              : 
     174            0 :     if (!m_libHandler) {
     175            0 :         return false;
     176              :     }
     177              : 
     178            0 :     return true;
     179              : }
     180              : 
     181            0 : AudioResourceMetaList MuseSamplerResolver::resolveResources() const
     182              : {
     183            0 :     AudioResourceMetaList result;
     184              : 
     185            0 :     if (!m_libHandler) {
     186            0 :         return result;
     187              :     }
     188              : 
     189            0 :     auto instrumentList = m_libHandler->getInstrumentList();
     190            0 :     while (auto instrument = m_libHandler->getNextInstrument(instrumentList)) {
     191            0 :         const int instrumentId = m_libHandler->getInstrumentId(instrument);
     192            0 :         const String internalName = String::fromUtf8(m_libHandler->getInstrumentName(instrument));
     193            0 :         const String internalCategory = String::fromUtf8(m_libHandler->getInstrumentCategory(instrument));
     194            0 :         const String instrumentSoundId = String::fromUtf8(m_libHandler->getMpeSoundId(instrument));
     195            0 :         const String vendorName = String::fromUtf8(m_libHandler->getInstrumentVendorName(instrument));
     196            0 :         const bool isOnline = m_libHandler->isOnlineInstrument(instrument);
     197              : 
     198            0 :         String instrumentPackName = String::fromUtf8(m_libHandler->getInstrumentPackName(instrument));
     199            0 :         if (instrumentPackName.empty()) {
     200            0 :             instrumentPackName = internalCategory;
     201              :         }
     202              : 
     203            0 :         AudioResourceMeta meta;
     204            0 :         meta.id = std::to_string(instrumentId);
     205            0 :         meta.type = AUDIO_RESOURCE_TYPE_NAME;
     206            0 :         meta.vendor = "MuseSounds";
     207            0 :         meta.attributes = {
     208              :             { u"playbackSetupData", instrumentSoundId },
     209              :             { u"museCategory", internalCategory },
     210              :             { u"musePack", instrumentPackName },
     211              :             { u"museVendorName", vendorName },
     212              :             { u"museName", internalName },
     213            0 :             { u"museUID", String::fromStdString(std::to_string(instrumentId)) },
     214            0 :         };
     215              : 
     216            0 :         if (isOnline) {
     217            0 :             meta.attributes.insert(std::make_pair(u"isOnline", String::number(1)));
     218              :         }
     219              : 
     220            0 :         result.push_back(std::move(meta));
     221            0 :     }
     222              : 
     223            0 :     return result;
     224            0 : }
     225              : 
     226            0 : SoundPresetList MuseSamplerResolver::resolveSoundPresets(const AudioResourceMeta& resourceMeta) const
     227              : {
     228            0 :     InstrumentInfo instrument = findInstrument(m_libHandler, resourceMeta);
     229            0 :     if (!instrument.msInstrument) {
     230            0 :         return SoundPresetList();
     231              :     }
     232              : 
     233            0 :     ms_PresetList presets = m_libHandler->getPresetList(instrument.msInstrument);
     234            0 :     SoundPresetList result;
     235              : 
     236            0 :     while (const char* presetCode = m_libHandler->getNextPreset(presets)) {
     237            0 :         SoundPreset soundPreset;
     238            0 :         soundPreset.code = presetCode;
     239            0 :         soundPreset.name = presetCode;
     240            0 :         loadSoundPresetAttributes(soundPreset.attributes, instrument.instrumentId, presetCode);
     241              : 
     242            0 :         result.emplace_back(std::move(soundPreset));
     243            0 :     }
     244              : 
     245            0 :     if (result.empty()) {
     246              :         // All instruments always have at least 1 preset (default)
     247              :         // If getPresetList returns an empty list, the default preset is implicitly defined as ""
     248            0 :         static const String DEFAULT_PRESET_CODE(u"Default");
     249              : 
     250            0 :         SoundPreset defaultPreset;
     251            0 :         defaultPreset.code = DEFAULT_PRESET_CODE;
     252            0 :         loadSoundPresetAttributes(defaultPreset.attributes, instrument.instrumentId, "");
     253              : 
     254            0 :         result.emplace_back(std::move(defaultPreset));
     255            0 :     }
     256              : 
     257            0 :     result.front().isDefault = true;
     258              : 
     259            0 :     return result;
     260            0 : }
     261              : 
     262            0 : void MuseSamplerResolver::refresh()
     263              : {
     264            0 : }
     265              : 
     266            0 : void MuseSamplerResolver::clearSources()
     267              : {
     268            0 : }
     269              : 
     270            0 : const Version& MuseSamplerResolver::version() const
     271              : {
     272            0 :     return m_samplerVersion;
     273              : }
     274              : 
     275            0 : bool MuseSamplerResolver::isLoaded() const
     276              : {
     277            0 :     if (m_libHandler) {
     278            0 :         return true;
     279              :     }
     280              : 
     281            0 :     return false;
     282              : }
     283              : 
     284            0 : bool MuseSamplerResolver::initFailed() const
     285              : {
     286            0 :     return m_initFailed;
     287              : }
     288              : 
     289            0 : float MuseSamplerResolver::defaultReverbLevel(const String& instrumentSoundId) const
     290              : {
     291            0 :     if (!m_libHandler || instrumentSoundId.empty()) {
     292            0 :         return 0.f;
     293              :     }
     294              : 
     295            0 :     auto instrumentList = m_libHandler->getInstrumentList();
     296            0 :     while (auto instrument = m_libHandler->getNextInstrument(instrumentList)) {
     297            0 :         String soundId = String::fromUtf8(m_libHandler->getMpeSoundId(instrument));
     298              : 
     299            0 :         if (instrumentSoundId == soundId) {
     300            0 :             return m_libHandler->getReverbLevel(instrument) / 100.f;
     301              :         }
     302            0 :     }
     303              : 
     304            0 :     return 0.f;
     305              : }
     306              : 
     307            0 : ByteArray MuseSamplerResolver::drumMapping(int instrumentId) const
     308              : {
     309            0 :     if (!m_libHandler) {
     310            0 :         return ByteArray();
     311              :     }
     312              : 
     313            0 :     const char* mapping_cstr = m_libHandler->getDrumMapping(instrumentId);
     314            0 :     return mapping_cstr ? ByteArray(mapping_cstr) : ByteArray();
     315              : }
     316              : 
     317            0 : std::vector<Instrument> MuseSamplerResolver::instruments() const
     318              : {
     319            0 :     if (!m_libHandler) {
     320            0 :         return {};
     321              :     }
     322              : 
     323            0 :     std::vector<Instrument> result;
     324              : 
     325            0 :     auto instrumentList = m_libHandler->getInstrumentList();
     326            0 :     while (auto msInstrument = m_libHandler->getNextInstrument(instrumentList)) {
     327            0 :         const char* json_cstr = m_libHandler->getInstrumentInfoJson(msInstrument);
     328            0 :         if (!json_cstr) {
     329            0 :             continue;
     330              :         }
     331              : 
     332            0 :         ByteArray json(json_cstr);
     333            0 :         if (json.empty()) {
     334            0 :             continue;
     335              :         }
     336              : 
     337            0 :         std::string err;
     338            0 :         JsonDocument doc = JsonDocument::fromJson(json, &err);
     339            0 :         if (!err.empty()) {
     340            0 :             LOGE() << err;
     341            0 :             continue;
     342              :         }
     343              : 
     344            0 :         int id = m_libHandler->getInstrumentId(msInstrument);
     345            0 :         JsonObject obj = doc.rootObject();
     346              : 
     347            0 :         Instrument instrument;
     348            0 :         instrument.id = String::number(id);
     349            0 :         instrument.soundId = String::fromUtf8(m_libHandler->getMpeSoundId(msInstrument));
     350            0 :         instrument.musicXmlId = String::fromUtf8(m_libHandler->getMusicXmlSoundId(msInstrument));
     351            0 :         instrument.name = obj.value("FriendlyName").toString();
     352            0 :         instrument.abbreviation = obj.value("Abbreviation").toString();
     353            0 :         instrument.category = obj.value("Category").toString();
     354            0 :         instrument.vendor = obj.value("Vendor").toString();
     355            0 :         instrument.staffLines = obj.value("StaffLines", "5").toString().toInt();
     356            0 :         instrument.staffType = muse::value(STAFF_NAME_LOWER_CASE_TO_TYPE, obj.value(
     357            0 :                                                "DefaultStaffType").toString().toLower(), StaffType::Standard);
     358            0 :         instrument.clefType = muse::value(CLEF_NAME_LOWER_CASE_TO_TYPE, obj.value("DefaultClef").toString().toLower(), ClefType::Treble);
     359              : 
     360            0 :         result.emplace_back(std::move(instrument));
     361            0 :     }
     362              : 
     363            0 :     return result;
     364            0 : }
     365              : 
     366            0 : void MuseSamplerResolver::loadSoundPresetAttributes(SoundPresetAttributes& attributes, int instrumentId, const char* presetCode) const
     367              : {
     368            0 :     const char* articulations_cstr = m_libHandler->getTextArticulations(instrumentId, presetCode);
     369            0 :     if (articulations_cstr) {
     370            0 :         String articulation = String::fromAscii(articulations_cstr);
     371              : 
     372            0 :         if (!articulation.empty()) {
     373            0 :             attributes.emplace(PLAYING_TECHNIQUES_ATTRIBUTE, std::move(articulation));
     374              :         }
     375            0 :     }
     376            0 : }
        

Generated by: LCOV version 2.0-1