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 : }
|