LCOV - code coverage report
Current view: top level - src/playback/qml/MuseScore/Playback - inputresourceitem.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 306 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) 2023 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 "inputresourceitem.h"
      23              : 
      24              : #include <optional>
      25              : 
      26              : #include <QList>
      27              : 
      28              : #include "log.h"
      29              : #include "stringutils.h"
      30              : #include "translation.h"
      31              : #include "types/string.h"
      32              : 
      33              : #include "audio/common/soundfonttypes.h"
      34              : #include "audio/common/audioutils.h"
      35              : 
      36              : #include "msbasicpresetscategories.h"
      37              : 
      38              : using namespace mu;
      39              : using namespace mu::playback;
      40              : using namespace muse;
      41              : using namespace muse::audio;
      42              : using namespace muse::audio::synth;
      43              : 
      44              : static const QString VST_MENU_ITEM_ID("VST3");
      45              : static const QString SOUNDFONTS_MENU_ITEM_ID("SoundFonts");
      46              : static const QString MUSE_MENU_ITEM_ID("MuseSounds");
      47              : static const QString GET_MORE_SOUNDS_ID("getMoreSounds");
      48              : 
      49              : static const muse::String MS_BASIC_SOUNDFONT_NAME(u"MS Basic");
      50              : 
      51              : static std::unordered_map<AudioResourceType, QString> AUDIO_RESOURCE_TYPE_TO_STR {
      52              :     { AudioResourceType::FluidSoundfont, SOUNDFONTS_MENU_ITEM_ID },
      53              :     { AudioResourceType::VstPlugin, VST_MENU_ITEM_ID },
      54              :     { AudioResourceType::MuseSamplerSoundPack, MUSE_MENU_ITEM_ID },
      55              : };
      56              : 
      57            0 : static QString makeMenuResourceItemId(AudioResourceType type, const QString& resourceId)
      58              : {
      59            0 :     QString str = muse::value(AUDIO_RESOURCE_TYPE_TO_STR, type);
      60            0 :     IF_ASSERT_FAILED(!str.isEmpty()) {
      61            0 :         return QString();
      62              :     }
      63              : 
      64            0 :     return str + "\\" + resourceId;
      65            0 : }
      66              : 
      67            0 : static void parseAudioResourceTypeAndId(const QString& menuItemId, AudioResourceType& type, audioplugins::PluginResourceId& resourceId)
      68              : {
      69            0 :     QString typeStr = menuItemId.section("\\", 0, 0);
      70            0 :     type = muse::key(AUDIO_RESOURCE_TYPE_TO_STR, typeStr);
      71            0 :     resourceId = menuItemId.section("\\", 1).toStdString();
      72            0 : }
      73              : 
      74            0 : InputResourceItem::InputResourceItem(QObject* parent)
      75            0 :     : AbstractAudioResourceItem(parent)
      76              : {
      77            0 : }
      78              : 
      79            0 : void InputResourceItem::requestAvailableResources()
      80              : {
      81            0 :     playback()->availableInputResources()
      82            0 :     .onResolve(this, [this](const AudioResourceMetaList& availableResources) {
      83            0 :         updateAvailableResources(availableResources);
      84              : 
      85            0 :         QVariantList result;
      86              : 
      87            0 :         if (!isBlank()) {
      88            0 :             QString currentResourceId = QString::fromStdString(m_currentInputParams.resourceMeta.id);
      89              : 
      90              :             result <<
      91            0 :                 buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(m_currentInputParams.resourceMeta.type),
      92              :                                                      currentResourceId),
      93            0 :                               title(),
      94            0 :                               /*checked*/ true, /*subItems*/ QVariantList(), /*includeInFilteredLists*/ false);
      95              : 
      96            0 :             result << buildSeparator();
      97            0 :         }
      98              : 
      99            0 :         auto museResourcesSearch = m_availableResourceMap.find(AudioResourceType::MuseSamplerSoundPack);
     100            0 :         if (museResourcesSearch != m_availableResourceMap.end()) {
     101            0 :             result << buildMuseMenuItem(museResourcesSearch->second);
     102              : 
     103            0 :             result << buildSeparator();
     104              :         }
     105              : 
     106            0 :         auto vstResourcesSearch = m_availableResourceMap.find(AudioResourceType::VstPlugin);
     107            0 :         if (vstResourcesSearch != m_availableResourceMap.end()) {
     108            0 :             result << buildVstMenuItem(vstResourcesSearch->second);
     109              : 
     110            0 :             result << buildSeparator();
     111              :         }
     112              : 
     113            0 :         auto sfResourcesSearch = m_availableResourceMap.find(AudioResourceType::FluidSoundfont);
     114            0 :         if (sfResourcesSearch != m_availableResourceMap.end()) {
     115            0 :             result << buildSoundFontsMenuItem(sfResourcesSearch->second);
     116              :         }
     117              : 
     118            0 :         result << buildSeparator();
     119            0 :         QVariantMap getMore =  buildExternalLinkMenuItem(GET_MORE_SOUNDS_ID, muse::qtrc("playback", "Get more sounds"));
     120            0 :         getMore["alwaysAppend"] = true; // Always add this to the end of our lists, ignoring filters
     121            0 :         result << getMore;
     122              : 
     123            0 :         emit availableResourceListResolved(result);
     124            0 :     })
     125            0 :     .onReject(this, [](const int errCode, const std::string& errText) {
     126            0 :         LOGE() << "Unable to resolve available output resources"
     127            0 :                << " , errCode:" << errCode
     128            0 :                << " , errText:" << errText;
     129            0 :     });
     130            0 : }
     131              : 
     132            0 : void InputResourceItem::handleMenuItem(const QString& menuItemId)
     133              : {
     134            0 :     if (menuItemId == GET_MORE_SOUNDS_ID) {
     135            0 :         const QString url = QString::fromStdString(globalConfiguration()->museHubWebUrl());
     136            0 :         const QString urlParams("muse-sounds?utm_source=mss-mixer&utm_medium=mh&utm_campaign=mss-mixer-ms-mainpage");
     137            0 :         platformInteractive()->openUrl(url + urlParams);
     138            0 :         return;
     139            0 :     }
     140              : 
     141            0 :     AudioResourceType newResourceType = AudioResourceType::Undefined;
     142            0 :     audioplugins::PluginResourceId newResourceId;
     143            0 :     parseAudioResourceTypeAndId(menuItemId, newResourceType, newResourceId);
     144              : 
     145            0 :     auto resourcesIt = m_availableResourceMap.find(newResourceType);
     146            0 :     if (resourcesIt == m_availableResourceMap.cend()) {
     147            0 :         LOGE() << "Resource not found: " << menuItemId;
     148            0 :         return;
     149              :     }
     150              : 
     151            0 :     for (const auto& pairByVendor : resourcesIt->second) {
     152            0 :         for (const AudioResourceMeta& resourceMeta : pairByVendor.second) {
     153            0 :             if (newResourceId != resourceMeta.id) {
     154            0 :                 continue;
     155              :             }
     156              : 
     157            0 :             if (m_currentInputParams.resourceMeta == resourceMeta) {
     158            0 :                 continue;
     159              :             }
     160              : 
     161            0 :             emit inputParamsChangeRequested(resourceMeta);
     162            0 :             return;
     163              :         }
     164              :     }
     165            0 : }
     166              : 
     167            0 : const AudioInputParams& InputResourceItem::params() const
     168              : {
     169            0 :     return m_currentInputParams;
     170              : }
     171              : 
     172            0 : void InputResourceItem::setParams(const audio::AudioInputParams& newParams)
     173              : {
     174            0 :     m_currentInputParams = newParams;
     175              : 
     176            0 :     emit titleChanged();
     177            0 :     emit isBlankChanged();
     178            0 :     emit isActiveChanged();
     179            0 : }
     180              : 
     181            0 : void InputResourceItem::setParamsRecourceMeta(const AudioResourceMeta& newMeta)
     182              : {
     183            0 :     requestToCloseNativeEditorView();
     184              : 
     185            0 :     m_currentInputParams.resourceMeta = newMeta;
     186            0 :     m_currentInputParams.configuration.clear();
     187              : 
     188            0 :     emit titleChanged();
     189            0 :     emit isBlankChanged();
     190            0 :     emit isActiveChanged();
     191            0 :     emit inputParamsChanged();
     192              : 
     193            0 :     requestToLaunchNativeEditorView();
     194            0 : }
     195              : 
     196            0 : QString InputResourceItem::title() const
     197              : {
     198            0 :     return audio::audioSourceName(m_currentInputParams).toQString();
     199              : }
     200              : 
     201            0 : bool InputResourceItem::isBlank() const
     202              : {
     203            0 :     return !m_currentInputParams.isValid();
     204              : }
     205              : 
     206            0 : bool InputResourceItem::isActive() const
     207              : {
     208            0 :     return m_currentInputParams.isValid();
     209              : }
     210              : 
     211            0 : bool InputResourceItem::hasNativeEditorSupport() const
     212              : {
     213            0 :     return muse::audio::hasNativeEditorSupport(m_currentInputParams.resourceMeta);
     214              : }
     215              : 
     216            0 : QVariantMap InputResourceItem::buildMuseMenuItem(const ResourceByVendorMap& resourcesByVendor) const
     217              : {
     218            0 :     QVariantList subItemsByType;
     219              : 
     220              :     // Vendor -> Pack -> Category -> Instruments
     221              :     using Instruments = std::vector<std::pair<std::string /*id*/, String /*name*/> >;
     222              :     using CategoryMap = std::map<String, Instruments>;
     223              :     using PackMap = std::map<String, CategoryMap>;
     224              :     using VendorMap = std::map<String, PackMap>;
     225              : 
     226            0 :     for (const auto& [_, metaList] : resourcesByVendor) {
     227            0 :         VendorMap vendorMap;
     228              : 
     229            0 :         for (const AudioResourceMeta& resourceMeta : metaList) {
     230            0 :             const muse::String& vendorName = resourceMeta.attributeVal(u"museVendorName");
     231            0 :             const muse::String& pack = resourceMeta.attributeVal(u"musePack");
     232            0 :             const muse::String& category = resourceMeta.attributeVal(u"museCategory");
     233            0 :             const muse::String& name = resourceMeta.attributeVal(u"museName");
     234              : 
     235            0 :             vendorMap[vendorName][pack][category].emplace_back(std::make_pair(resourceMeta.id, name));
     236              :         }
     237              : 
     238            0 :         for (const auto& [vendorName, packs] : vendorMap) {
     239            0 :             QVariantList subItemsByVendor;
     240            0 :             bool isCurrentVendor = false;
     241              : 
     242            0 :             for (const auto& [packName, categories] : packs) {
     243            0 :                 QVariantList subItemsByPack;
     244            0 :                 bool isCurrentPack = false;
     245              : 
     246            0 :                 for (const auto& [categoryName, instruments] : categories) {
     247            0 :                     QVariantList subItemsByCategory;
     248            0 :                     bool isCurrentCategory = false;
     249              : 
     250            0 :                     for (const auto& [instId, instName] : instruments) {
     251            0 :                         bool isCurrentInstrument = m_currentInputParams.resourceMeta.id == instId;
     252            0 :                         QString itemId = makeMenuResourceItemId(AudioResourceType::MuseSamplerSoundPack, QString::fromStdString(instId));
     253              : 
     254            0 :                         subItemsByCategory << buildMenuItem(itemId, instName.toQString(), isCurrentInstrument);
     255            0 :                         isCurrentCategory = isCurrentCategory || isCurrentInstrument;
     256            0 :                     }
     257              : 
     258              :                     // Create submenu only if there are 2 or more categories
     259            0 :                     if (categories.size() > 1 && !categoryName.empty()) {
     260            0 :                         QString categoryString = categoryName.toQString();
     261            0 :                         subItemsByPack << buildMenuItem(categoryString,
     262              :                                                         categoryString,
     263              :                                                         isCurrentCategory,
     264              :                                                         subItemsByCategory,
     265            0 :                                                         /*includeInFilteredLists*/ false);
     266            0 :                     } else {
     267            0 :                         subItemsByPack << subItemsByCategory;
     268              :                     }
     269              : 
     270            0 :                     isCurrentPack = isCurrentPack || isCurrentCategory;
     271            0 :                 }
     272              : 
     273              :                 // Create submenu only if there are 2 or more packs
     274            0 :                 if (packs.size() > 1 && !packName.empty()) {
     275            0 :                     QString packString = packName.toQString();
     276            0 :                     subItemsByVendor << buildMenuItem(packString,
     277              :                                                       packString,
     278              :                                                       isCurrentPack,
     279              :                                                       subItemsByPack,
     280              :                                                       /*includeInFilteredLists*/ false,
     281            0 :                                                       /*isFilterCategory*/ true);
     282            0 :                 } else {
     283            0 :                     subItemsByVendor << subItemsByPack;
     284              :                 }
     285              : 
     286            0 :                 isCurrentVendor = isCurrentVendor || isCurrentPack;
     287            0 :             }
     288              : 
     289            0 :             if (!vendorName.empty()) {
     290            0 :                 QString vendorString = vendorName.toQString();
     291            0 :                 subItemsByType << buildMenuItem(vendorString,
     292              :                                                 vendorString,
     293              :                                                 isCurrentVendor,
     294              :                                                 subItemsByVendor,
     295              :                                                 /*includeInFilteredLists*/ false,
     296            0 :                                                 /*isFilterCategory*/ true);
     297            0 :             } else {
     298            0 :                 subItemsByType << subItemsByVendor;
     299              :             }
     300            0 :         }
     301            0 :     }
     302              : 
     303              :     return buildMenuItem(MUSE_MENU_ITEM_ID,
     304              :                          MUSE_MENU_ITEM_ID,
     305            0 :                          muse::audio::isResourceType(m_currentInputParams.resourceMeta,
     306              :                                                      muse::audio::AudioResourceType::MuseSamplerSoundPack),
     307              :                          subItemsByType,
     308            0 :                          /*includeInFilteredLists*/ false);
     309            0 : }
     310              : 
     311            0 : QVariantMap InputResourceItem::buildVstMenuItem(const ResourceByVendorMap& resourcesByVendor) const
     312              : {
     313            0 :     QVariantList subItemsByType;
     314              : 
     315            0 :     for (const auto& pair : resourcesByVendor) {
     316            0 :         QVariantList subItemsByVendor;
     317              : 
     318            0 :         for (const AudioResourceMeta& resourceMeta : pair.second) {
     319            0 :             QString resourceId = QString::fromStdString(resourceMeta.id);
     320            0 :             subItemsByVendor << buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(resourceMeta.type), resourceId),
     321              :                                               resourceId,
     322            0 :                                               m_currentInputParams.resourceMeta.id == resourceMeta.id);
     323            0 :         }
     324              : 
     325            0 :         QString vendor = QString::fromStdString(pair.first);
     326            0 :         subItemsByType << buildMenuItem(vendor,
     327              :                                         vendor,
     328            0 :                                         m_currentInputParams.resourceMeta.vendor == pair.first,
     329              :                                         subItemsByVendor,
     330              :                                         /*includeInFilteredLists*/ false,
     331            0 :                                         /*isFilterCategory*/ true);
     332            0 :     }
     333              : 
     334              :     return buildMenuItem(VST_MENU_ITEM_ID,
     335              :                          VST_MENU_ITEM_ID,
     336            0 :                          muse::audio::isResourceType(m_currentInputParams.resourceMeta,
     337              :                                                      muse::audio::AudioResourceType::VstPlugin),
     338              :                          subItemsByType,
     339            0 :                          /*includeInFilteredLists*/ false);
     340            0 : }
     341              : 
     342            0 : QVariantMap InputResourceItem::buildSoundFontsMenuItem(const ResourceByVendorMap& resourcesByVendor) const
     343              : {
     344              :     // Get info about current resource
     345            0 :     const muse::String& currentSoundFontName = m_currentInputParams.resourceMeta.attributeVal(SOUNDFONT_NAME_ATTRIBUTE);
     346            0 :     std::optional<midi::Program> currentPreset = std::nullopt;
     347              :     {
     348            0 :         if (!currentSoundFontName.empty()) {
     349            0 :             bool bankOk = false, programOk = false;
     350            0 :             int currentPresetBank = m_currentInputParams.resourceMeta.attributeVal(PRESET_BANK_ATTRIBUTE).toInt(&bankOk);
     351            0 :             int currentPresetProgram = m_currentInputParams.resourceMeta.attributeVal(PRESET_PROGRAM_ATTRIBUTE).toInt(&programOk);
     352              : 
     353            0 :             if (bankOk && programOk) {
     354            0 :                 currentPreset = midi::Program(currentPresetBank, currentPresetProgram);
     355              :             }
     356              :         }
     357              :     }
     358              : 
     359              :     // Group resources by SoundFont name
     360            0 :     std::map<muse::String, AudioResourceMetaList> resourcesBySoundFont;
     361              : 
     362            0 :     for (const auto& pair : resourcesByVendor) {
     363            0 :         for (const AudioResourceMeta& resourceMeta : pair.second) {
     364            0 :             const muse::String& soundFontName = resourceMeta.attributeVal(SOUNDFONT_NAME_ATTRIBUTE);
     365              : 
     366            0 :             resourcesBySoundFont[soundFontName].push_back(resourceMeta);
     367              :         }
     368              :     }
     369              : 
     370              :     // Sort SoundFonts by name and add them to the menu
     371            0 :     std::vector<muse::String> soundFonts;
     372            0 :     soundFonts.reserve(resourcesBySoundFont.size());
     373            0 :     for (const auto& pair : resourcesBySoundFont) {
     374            0 :         soundFonts.push_back(pair.first);
     375              :     }
     376              : 
     377            0 :     std::sort(soundFonts.begin(), soundFonts.end(), [](const muse::String& s1, const muse::String& s2) {
     378            0 :         return muse::strings::lessThanCaseInsensitive(s1, s2);
     379              :     });
     380              : 
     381            0 :     QVariantList soundFontItems;
     382            0 :     muse::String currentSoundFontId = muse::String::fromStdString(m_currentInputParams.resourceMeta.id);
     383              : 
     384            0 :     for (const muse::String& soundFont : soundFonts) {
     385              :         // currentSoundFontId will be equal to soundFont in the case of "choose automatically" for older files (this is a temporary fix)
     386              :         // See: https://github.com/musescore/MuseScore/pull/20316#issuecomment-1841326774
     387            0 :         bool isCurrentSoundFont = currentSoundFontName == soundFont || currentSoundFontId == soundFont;
     388              : 
     389            0 :         if (soundFont == MS_BASIC_SOUNDFONT_NAME) {
     390            0 :             soundFontItems << buildMsBasicMenuItem(resourcesBySoundFont[soundFont], isCurrentSoundFont, currentPreset);
     391              :         } else {
     392            0 :             soundFontItems << buildSoundFontMenuItem(soundFont, resourcesBySoundFont[soundFont], isCurrentSoundFont, currentPreset);
     393              :         }
     394              :     }
     395              : 
     396              :     return buildMenuItem(SOUNDFONTS_MENU_ITEM_ID,
     397            0 :                          muse::qtrc("playback", "SoundFonts"),
     398            0 :                          muse::audio::isResourceType(m_currentInputParams.resourceMeta,
     399              :                                                      muse::audio::AudioResourceType::FluidSoundfont),
     400              :                          soundFontItems,
     401            0 :                          /*includeInFilteredLists*/ false);
     402            0 : }
     403              : 
     404            0 : QVariantMap InputResourceItem::buildMsBasicMenuItem(const AudioResourceMetaList& availableResources, bool isCurrentSoundFont,
     405              :                                                     const std::optional<midi::Program>& currentPreset) const
     406              : {
     407            0 :     std::map<midi::Program, AudioResourceMeta> resourcesByProgram;
     408            0 :     AudioResourceMeta chooseAutomaticMeta;
     409              : 
     410            0 :     for (const AudioResourceMeta& resourceMeta : availableResources) {
     411            0 :         bool bankOk = false, programOk = false;
     412            0 :         int presetBank = resourceMeta.attributeVal(PRESET_BANK_ATTRIBUTE).toInt(&bankOk);
     413            0 :         int presetProgram = resourceMeta.attributeVal(PRESET_PROGRAM_ATTRIBUTE).toInt(&programOk);
     414              : 
     415            0 :         if (bankOk && programOk) {
     416            0 :             resourcesByProgram[midi::Program(presetBank, presetProgram)] = resourceMeta;
     417              :         } else {
     418            0 :             chooseAutomaticMeta = resourceMeta;
     419              :         }
     420              :     }
     421              : 
     422              :     std::function<QVariantMap(const MsBasicItem&, const QString&, bool&, bool&)> buildMsBasicItem
     423            0 :         = [&](const MsBasicItem& item, const QString& parentMenuId, bool& ok, bool& isCurrent) {
     424            0 :         ok = true;
     425            0 :         if (item.subItems.empty()) {
     426            0 :             auto it = resourcesByProgram.find(item.preset);
     427            0 :             if (it == resourcesByProgram.cend()) {
     428            0 :                 LOGW() << "Preset specified in MS_BASIC_PRESET_CATEGORIES not found in SoundFont: bank " << item.preset.bank
     429            0 :                        << ", program " << item.preset.program;
     430              : 
     431            0 :                 ok = false;
     432            0 :                 return QVariantMap();
     433              :             }
     434              : 
     435            0 :             const AudioResourceMeta& resourceMeta = it->second;
     436              : 
     437            0 :             isCurrent = isCurrentSoundFont && currentPreset.has_value() && currentPreset.value() == item.preset;
     438              : 
     439            0 :             QString presetName = resourceMeta.attributeVal(PRESET_NAME_ATTRIBUTE);
     440            0 :             if (presetName.isEmpty()) {
     441            0 :                 presetName = muse::qtrc("playback", "Bank %1, preset %2").arg(item.preset.bank).arg(item.preset.program);
     442              :             }
     443              : 
     444            0 :             return buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(resourceMeta.type),
     445            0 :                                                         QString::fromStdString(resourceMeta.id)),
     446              :                                  presetName,
     447            0 :                                  isCurrent);
     448            0 :         }
     449              : 
     450            0 :         QString menuId = parentMenuId + "\\" + item.title + "\\menu";
     451              : 
     452            0 :         QVariantList subItems;
     453              : 
     454            0 :         for (const MsBasicItem& subItem : item.subItems) {
     455            0 :             bool _ok = false;
     456            0 :             bool isSubItemCurrent = false;
     457              : 
     458            0 :             QVariantMap menuItem = buildMsBasicItem(subItem, parentMenuId, _ok, isSubItemCurrent);
     459            0 :             if (!_ok) {
     460            0 :                 continue;
     461              :             }
     462              : 
     463              :             // Temporary fix, see: https://github.com/musescore/MuseScore/issues/20142
     464            0 :             muse::String title = menuItem.value("title").toString();
     465            0 :             if (title.contains(muse::String(u"Expr."))) {
     466            0 :                 continue;
     467              :             }
     468              : 
     469            0 :             subItems << menuItem;
     470              : 
     471            0 :             if (isSubItemCurrent) {
     472            0 :                 isCurrent = true;
     473              :             }
     474            0 :         }
     475              : 
     476            0 :         return buildMenuItem(menuId,
     477              :                              item.title,
     478            0 :                              isCurrent,
     479              :                              subItems,
     480            0 :                              /*includeInFilteredLists*/ false);
     481            0 :     };
     482              : 
     483            0 :     QString menuId = MS_BASIC_SOUNDFONT_NAME.toQString() + "\\menu";
     484              : 
     485            0 :     QVariantList categoryItems;
     486              : 
     487            0 :     for (const MsBasicItem& category : MS_BASIC_PRESET_CATEGORIES) {
     488            0 :         bool ok = false;
     489            0 :         bool isCurrent = false;
     490              : 
     491            0 :         categoryItems << buildMsBasicItem(category, menuId, ok, isCurrent);
     492              :     }
     493              : 
     494              :     // Prepend the "Choose automatically" item
     495            0 :     categoryItems.prepend(buildSeparator());
     496            0 :     categoryItems.prepend(buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(chooseAutomaticMeta.type),
     497            0 :                                                                QString::fromStdString(chooseAutomaticMeta.id)),
     498            0 :                                         muse::qtrc("playback", "Choose automatically"),
     499            0 :                                         isCurrentSoundFont && !currentPreset.has_value(),
     500            0 :                                         /*subItems*/ QVariantList(),
     501              :                                         /*includeInFilteredLists*/ false));
     502              : 
     503            0 :     return buildMenuItem(menuId,
     504              :                          MS_BASIC_SOUNDFONT_NAME,
     505              :                          isCurrentSoundFont,
     506              :                          categoryItems,
     507              :                          /*includeInFilteredLists*/ false,
     508            0 :                          /*isFilterCategory*/ true);
     509            0 : }
     510              : 
     511            0 : QVariantMap InputResourceItem::buildSoundFontMenuItem(const muse::String& soundFont,
     512              :                                                       const audio::AudioResourceMetaList& availableResources,
     513              :                                                       bool isCurrentSoundFont,
     514              :                                                       const std::optional<midi::Program>& currentPreset) const
     515              : {
     516              :     // Group resources by bank, and use this to sort them
     517            0 :     std::map<int, std::map<int, AudioResourceMeta> > resourcesByBank;
     518            0 :     AudioResourceMeta chooseAutomaticMeta;
     519              : 
     520            0 :     for (const AudioResourceMeta& resourceMeta : availableResources) {
     521            0 :         bool bankOk = false, programOk = false;
     522            0 :         int presetBank = resourceMeta.attributeVal(PRESET_BANK_ATTRIBUTE).toInt(&bankOk);
     523            0 :         int presetProgram = resourceMeta.attributeVal(PRESET_PROGRAM_ATTRIBUTE).toInt(&programOk);
     524              : 
     525            0 :         if (bankOk && programOk) {
     526            0 :             resourcesByBank[presetBank][presetProgram] = resourceMeta;
     527              :         } else {
     528            0 :             chooseAutomaticMeta = resourceMeta;
     529              :         }
     530              :     }
     531              : 
     532            0 :     QVariantList bankItems;
     533              : 
     534            0 :     for (const auto& bankPair : resourcesByBank) {
     535            0 :         bool isCurrentBank = isCurrentSoundFont && currentPreset.has_value() && currentPreset.value().bank == bankPair.first;
     536              : 
     537            0 :         QVariantList presetItems;
     538              : 
     539            0 :         for (const auto& presetPair : bankPair.second) {
     540            0 :             bool isCurrentPreset = isCurrentBank && currentPreset.value().program == presetPair.first;
     541              : 
     542            0 :             QString presetName = presetPair.second.attributeVal(PRESET_NAME_ATTRIBUTE);
     543            0 :             if (presetName.isEmpty()) {
     544            0 :                 presetName = muse::qtrc("playback", "Preset %1").arg(presetPair.first);
     545              :             }
     546              : 
     547            0 :             QString itemId = makeMenuResourceItemId(AudioResourceType::FluidSoundfont, QString::fromStdString(presetPair.second.id));
     548            0 :             presetItems << buildMenuItem(itemId,
     549              :                                          presetName,
     550            0 :                                          isCurrentPreset);
     551            0 :         }
     552              : 
     553            0 :         bankItems << buildMenuItem(soundFont + u"\\" + muse::String::number(bankPair.first),
     554            0 :                                    muse::qtrc("playback", "Bank %1").arg(bankPair.first),
     555              :                                    isCurrentBank,
     556              :                                    presetItems,
     557            0 :                                    /*includeInFilteredLists*/ false);
     558            0 :     }
     559              : 
     560              :     // Prepend the "Choose automatically" item
     561            0 :     bankItems.prepend(buildSeparator());
     562            0 :     bankItems.prepend(buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(chooseAutomaticMeta.type),
     563            0 :                                                            QString::fromStdString(chooseAutomaticMeta.id)),
     564            0 :                                     muse::qtrc("playback", "Choose automatically"),
     565            0 :                                     isCurrentSoundFont && !currentPreset.has_value(),
     566            0 :                                     /*subItems*/ QVariantList(),
     567              :                                     /*includeInFilteredLists*/ false));
     568              : 
     569            0 :     return buildMenuItem(soundFont + u"\\menu",
     570              :                          soundFont,
     571              :                          isCurrentSoundFont,
     572              :                          bankItems,
     573              :                          /*includeInFilteredLists*/ false,
     574            0 :                          /*isFilterCategory*/ true);
     575            0 : }
     576              : 
     577            0 : void InputResourceItem::updateAvailableResources(const AudioResourceMetaList& availableResources)
     578              : {
     579            0 :     m_availableResourceMap.clear();
     580              : 
     581            0 :     for (const AudioResourceMeta& meta : availableResources) {
     582            0 :         ResourceByVendorMap& resourcesByVendor = m_availableResourceMap[muse::audio::resourceTypeFromString(meta.type)];
     583            0 :         AudioResourceMetaList& resourcesMetaList = resourcesByVendor[meta.vendor];
     584            0 :         resourcesMetaList.push_back(meta);
     585              :     }
     586              : 
     587            0 :     for (auto& [type, resourcesByVendor] : m_availableResourceMap) {
     588            0 :         for (auto& [vendor, resourceMetaList] : resourcesByVendor) {
     589            0 :             sortResourcesList(resourceMetaList);
     590              :         }
     591              :     }
     592            0 : }
        

Generated by: LCOV version 2.0-1