From 41eade9f0f0e25f0c56469170d1a3024714fffb2 Mon Sep 17 00:00:00 2001 From: Igor Korsukov Date: Wed, 13 May 2026 11:38:40 +0200 Subject: [PATCH 01/10] [ci] updated check codestyle on CI --- muse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/muse b/muse index e573f8bc57ab1..b8ca198cc172b 160000 --- a/muse +++ b/muse @@ -1 +1 @@ -Subproject commit e573f8bc57ab1373d0a80b013e3d3fed932fb7f4 +Subproject commit b8ca198cc172b48ed6a7c4f2f0656f33c2225cc7 From a791f63b6fb485f2b9b544e062e96077997e17d2 Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Tue, 12 May 2026 18:21:18 +0200 Subject: [PATCH 02/10] [audioplugins] adapt MuseScore to framework audioplugins revamp Companion to the muse_framework audioplugins revamp: - New AudioPluginsAppConfigModule wires v0->v1 (hasNativeEditorSupport into attributes) and v1->v2 (enabled boolean -> state string) cache migrations plus the runtime attribute defaults. - Adopts the new typed plugin format helpers (audio::isResourceType, audio::resourceTypeFromString, audio::hasNativeEditorSupport) at call sites in playback, project, notationscene, converter. - Consumes vst::CATEGORIES_ATTRIBUTE (moved out of the audio module). --- src/app/CMakeLists.txt | 3 + src/app/appfactory.cpp | 4 + .../internal/audiopluginsappconfigmodule.cpp | 111 ++++++++++++++++++ .../internal/audiopluginsappconfigmodule.h | 33 ++++++ .../internal/compat/notationmeta.cpp | 4 +- .../percussionpanel/percussionpanelmodel.cpp | 4 +- src/playback/internal/drumsetloader.cpp | 4 +- src/playback/internal/playbackcontroller.cpp | 2 +- .../internal/soundprofilesrepository.cpp | 4 +- .../MuseScore/Playback/inputresourceitem.cpp | 29 +++-- .../MuseScore/Playback/outputresourceitem.cpp | 5 +- src/project/internal/projectaudiosettings.cpp | 20 ++-- src/project/internal/projectaudiosettings.h | 2 +- 13 files changed, 194 insertions(+), 31 deletions(-) create mode 100644 src/app/internal/audiopluginsappconfigmodule.cpp create mode 100644 src/app/internal/audiopluginsappconfigmodule.h diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index fe5bd214e39cc..c63c0340ba2d0 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -217,6 +217,9 @@ set(APP_SRC appfactory.cpp appfactory.h + internal/audiopluginsappconfigmodule.cpp + internal/audiopluginsappconfigmodule.h + internal/guiapp.cpp internal/guiapp.h ) diff --git a/src/app/appfactory.cpp b/src/app/appfactory.cpp index fff9a369d9225..8c5c027cec21a 100644 --- a/src/app/appfactory.cpp +++ b/src/app/appfactory.cpp @@ -26,6 +26,7 @@ #ifdef MUSE_MODULE_AUDIOPLUGINS #include "framework/audioplugins/audiopluginsmodule.h" +#include "internal/audiopluginsappconfigmodule.h" #endif #ifdef MUSE_MODULE_CLOUD @@ -318,6 +319,7 @@ std::shared_ptr AppFactory::newGuiApp(const std::shared_ptr< app->addModule(new muse::audio::AudioModule()); #ifdef MUSE_MODULE_AUDIOPLUGINS app->addModule(new muse::audioplugins::AudioPluginsModule()); + app->addModule(new mu::app::AudioPluginsAppConfigModule()); #endif app->addModule(new muse::automation::AutomationModule()); app->addModule(new muse::draw::DrawModule()); @@ -461,6 +463,7 @@ static void addConsoleModules(std::shared_ptr app) app->addModule(new muse::audio::AudioModule()); #ifdef MUSE_MODULE_AUDIOPLUGINS app->addModule(new muse::audioplugins::AudioPluginsModule()); + app->addModule(new mu::app::AudioPluginsAppConfigModule()); #endif app->addModule(new muse::draw::DrawModule()); app->addModule(new muse::midi::MidiModule()); @@ -553,6 +556,7 @@ static void addAudioPluginRegistrationModules(std::shared_ptraddModule(new muse::audioplugins::AudioPluginsModule()); + app->addModule(new mu::app::AudioPluginsAppConfigModule()); #endif #ifdef MUSE_MODULE_VST diff --git a/src/app/internal/audiopluginsappconfigmodule.cpp b/src/app/internal/audiopluginsappconfigmodule.cpp new file mode 100644 index 0000000000000..40c90068bb352 --- /dev/null +++ b/src/app/internal/audiopluginsappconfigmodule.cpp @@ -0,0 +1,111 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2026 MuseScore Limited and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "audiopluginsappconfigmodule.h" + +#include "modularity/ioc.h" + +#include "global/serialization/json.h" + +#include "audio/common/audiotypes.h" +#include "mpe/playbacksetupdata.h" + +#include "audioplugins/iaudiopluginsconfiguration.h" +#include "audioplugins/iknownaudiopluginsmigrationregister.h" + +using namespace mu::app; +using namespace muse::audioplugins; + +static const std::string mname("audiopluginsappconfig"); + +std::string AudioPluginsAppConfigModule::moduleName() const +{ + return mname; +} + +void AudioPluginsAppConfigModule::resolveImports() +{ + auto configuration = muse::modularity::globalIoc()->resolve(moduleName()); + if (configuration) { + // MuseScore's audio engine routes synthesis via the playbackSetupData + // attribute. It is runtime-only — the engine re-injects the generic + // default at every load, so the cache file should not persist it. + AudioResourceAttributes runtimeDefaults; + runtimeDefaults.emplace(muse::audio::PLAYBACK_SETUP_DATA_ATTRIBUTE, + muse::mpe::GENERIC_SETUP_DATA_STRING); + configuration->setRuntimeAttributeDefaults(runtimeDefaults); + } + + auto migrations = muse::modularity::globalIoc()->resolve(moduleName()); + if (migrations) { + // v0 → v1: hasNativeEditorSupport moved from a top-level meta field + // into meta.attributes (string "true"/"false"). + migrations->registerMigration(0, [](const muse::JsonArray& plugins) { + muse::JsonArray out; + for (size_t i = 0; i < plugins.size(); ++i) { + muse::JsonObject obj = plugins.at(i).toObject(); + muse::JsonObject meta = obj.value("meta").toObject(); + if (meta.contains("hasNativeEditorSupport")) { + muse::JsonObject attrs; + if (meta.contains("attributes")) { + attrs = meta.value("attributes").toObject(); + } + const bool b = meta.value("hasNativeEditorSupport").toBool(); + attrs.set("hasNativeEditorSupport", b ? std::string("true") : std::string("false")); + meta.set("attributes", attrs); + + // JsonObject has no remove(); rebuild without the legacy key. + muse::JsonObject metaWithoutLegacy; + for (const std::string& k : meta.keys()) { + if (k == "hasNativeEditorSupport") { + continue; + } + metaWithoutLegacy.set(k, meta.value(k)); + } + obj.set("meta", metaWithoutLegacy); + } + out << obj; + } + return out; + }); + + // v1 → v2: replace the boolean `enabled` flag with a `state` string + // ("Validated" / "Error"). errorCode is preserved. + migrations->registerMigration(1, [](const muse::JsonArray& plugins) { + muse::JsonArray out; + for (size_t i = 0; i < plugins.size(); ++i) { + muse::JsonObject obj = plugins.at(i).toObject(); + const bool enabled = obj.value("enabled").toBool(); + obj.set("state", enabled ? std::string("Validated") : std::string("Error")); + + muse::JsonObject rebuilt; + for (const std::string& k : obj.keys()) { + if (k == "enabled") { + continue; + } + rebuilt.set(k, obj.value(k)); + } + out << rebuilt; + } + return out; + }); + } +} diff --git a/src/app/internal/audiopluginsappconfigmodule.h b/src/app/internal/audiopluginsappconfigmodule.h new file mode 100644 index 0000000000000..c5c959f81adb6 --- /dev/null +++ b/src/app/internal/audiopluginsappconfigmodule.h @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2026 MuseScore Limited and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "modularity/imodulesetup.h" + +namespace mu::app { +class AudioPluginsAppConfigModule : public muse::modularity::IModuleSetup +{ +public: + std::string moduleName() const override; + void resolveImports() override; +}; +} diff --git a/src/converter/internal/compat/notationmeta.cpp b/src/converter/internal/compat/notationmeta.cpp index 104e899a3040f..1db7154f31c65 100644 --- a/src/converter/internal/compat/notationmeta.cpp +++ b/src/converter/internal/compat/notationmeta.cpp @@ -378,9 +378,9 @@ QJsonArray NotationMeta::tracksJsonArray(notation::INotationPtr notation) QJsonObject jsonTrack; jsonTrack.insert("instrumentId", trackId.instrumentId.toQString()); jsonTrack.insert("partId", trackId.partId.toQString()); - jsonTrack.insert("type", audioResourceTypeToString(inputParams.resourceMeta.type).toQString()); + jsonTrack.insert("type", audio::audioResourceTypeToString(inputParams.resourceMeta.type).toQString()); - audio::AudioSourceType sourceType = sourceTypeFromResourceType(inputParams.resourceMeta.type); + audio::AudioSourceType sourceType = audio::sourceTypeFromResourceType(inputParams.resourceMeta.type); if (sourceType != audio::AudioSourceType::Fluid) { if (sourceType == audio::AudioSourceType::MuseSampler) { jsonTrack.insert("vendor", QString::fromStdString(inputParams.resourceMeta.attributeVal( diff --git a/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp b/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp index 6ebd7ab61a776..c81936c4755a6 100644 --- a/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp +++ b/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp @@ -553,7 +553,7 @@ void PercussionPanelModel::resetLayout() } const muse::audio::AudioResourceMeta& resourceMeta = audioSettings()->trackInputParams(currentTrackId()).resourceMeta; - const bool isMuseSamplerDrumset = resourceMeta.type == muse::audio::AudioResourceType::MuseSamplerSoundPack; + const bool isMuseSamplerDrumset = resourceMeta.type == "MuseSamplerSoundPack"; Drumset defaultDrumset = isMuseSamplerDrumset ? museSamplerDefaultDrumset() : standardDefaultDrumset(); @@ -593,7 +593,7 @@ Drumset PercussionPanelModel::museSamplerDefaultDrumset() const const muse::audio::AudioResourceMeta& resourceMeta = audioSettings()->trackInputParams(currentTrackId()).resourceMeta; - const int instrumentId = resourceMeta.attributeVal(u"museUID").toInt(); + const int instrumentId = muse::audioplugins::intAttribute(resourceMeta, u"museUID"); const muse::ByteArray drumMapping = museSampler()->drumMapping(instrumentId); IF_ASSERT_FAILED(!drumMapping.empty()) { diff --git a/src/playback/internal/drumsetloader.cpp b/src/playback/internal/drumsetloader.cpp index 3e4d336b1cc1c..c94d1c2c314ce 100644 --- a/src/playback/internal/drumsetloader.cpp +++ b/src/playback/internal/drumsetloader.cpp @@ -41,7 +41,7 @@ void DrumsetLoader::loadDrumset(INotationPtr notation, const InstrumentTrackId& } // restore the default drumset when changing from MuseSounds to MS Basic / VST - if (resourceMeta.type != AudioResourceType::MuseSamplerSoundPack) { + if (resourceMeta.type != "MuseSamplerSoundPack") { const InstrumentTemplate& templ = instrumentsRepository()->instrumentTemplate(trackId.instrumentId); if (!templ.useDrumset) { return; @@ -56,7 +56,7 @@ void DrumsetLoader::loadDrumset(INotationPtr notation, const InstrumentTrackId& return; } - int instrumentId = resourceMeta.attributeVal(u"museUID").toInt(); + int instrumentId = muse::audioplugins::intAttribute(resourceMeta, u"museUID"); auto it = m_drumsetCache.find(instrumentId); if (it != m_drumsetCache.end()) { diff --git a/src/playback/internal/playbackcontroller.cpp b/src/playback/internal/playbackcontroller.cpp index 5cb6641790b6e..26198d701a5b9 100644 --- a/src/playback/internal/playbackcontroller.cpp +++ b/src/playback/internal/playbackcontroller.cpp @@ -493,7 +493,7 @@ bool PlaybackController::shouldLoadDrumset(const engraving::InstrumentTrackId& i return false; } - return oldMeta.type == AudioResourceType::MuseSamplerSoundPack || newMeta.type == AudioResourceType::MuseSamplerSoundPack; + return oldMeta.type == "MuseSamplerSoundPack" || newMeta.type == "MuseSamplerSoundPack"; } void PlaybackController::addSoundFlagsIfNeed(const std::vector& selection) diff --git a/src/playback/internal/soundprofilesrepository.cpp b/src/playback/internal/soundprofilesrepository.cpp index 7e6eca6cd6800..25a96823ce63a 100644 --- a/src/playback/internal/soundprofilesrepository.cpp +++ b/src/playback/internal/soundprofilesrepository.cpp @@ -67,11 +67,11 @@ void SoundProfilesRepository::refresh() continue; } - if (resource.type == AudioResourceType::FluidSoundfont) { + if (resource.type == "FluidSoundfont") { basicProfile.data.emplace(mpe::PlaybackSetupData::fromString(setup->second), resource); } - if (resource.type == AudioResourceType::MuseSamplerSoundPack) { + if (resource.type == "MuseSamplerSoundPack") { museProfile.data.emplace(mpe::PlaybackSetupData::fromString(setup->second), resource); } } diff --git a/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp b/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp index 7b2b68dc41b1c..ff0f24c034153 100644 --- a/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp +++ b/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp @@ -87,9 +87,11 @@ void InputResourceItem::requestAvailableResources() if (!isBlank()) { QString currentResourceId = QString::fromStdString(m_currentInputParams.resourceMeta.id); - result << buildMenuItem(makeMenuResourceItemId(m_currentInputParams.resourceMeta.type, currentResourceId), - title(), - /*checked*/ true, /*subItems*/ QVariantList(), /*includeInFilteredLists*/ false); + result << + buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(m_currentInputParams.resourceMeta.type), + currentResourceId), + title(), + /*checked*/ true, /*subItems*/ QVariantList(), /*includeInFilteredLists*/ false); result << buildSeparator(); } @@ -208,7 +210,7 @@ bool InputResourceItem::isActive() const bool InputResourceItem::hasNativeEditorSupport() const { - return m_currentInputParams.resourceMeta.hasNativeEditorSupport; + return muse::audio::hasNativeEditorSupport(m_currentInputParams.resourceMeta); } QVariantMap InputResourceItem::buildMuseMenuItem(const ResourceByVendorMap& resourcesByVendor) const @@ -300,7 +302,7 @@ QVariantMap InputResourceItem::buildMuseMenuItem(const ResourceByVendorMap& reso return buildMenuItem(MUSE_MENU_ITEM_ID, MUSE_MENU_ITEM_ID, - m_currentInputParams.resourceMeta.type == AudioResourceType::MuseSamplerSoundPack, + m_currentInputParams.resourceMeta.type == "MuseSamplerSoundPack", subItemsByType, /*includeInFilteredLists*/ false); } @@ -314,7 +316,7 @@ QVariantMap InputResourceItem::buildVstMenuItem(const ResourceByVendorMap& resou for (const AudioResourceMeta& resourceMeta : pair.second) { QString resourceId = QString::fromStdString(resourceMeta.id); - subItemsByVendor << buildMenuItem(makeMenuResourceItemId(resourceMeta.type, resourceId), + subItemsByVendor << buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(resourceMeta.type), resourceId), resourceId, m_currentInputParams.resourceMeta.id == resourceMeta.id); } @@ -330,7 +332,7 @@ QVariantMap InputResourceItem::buildVstMenuItem(const ResourceByVendorMap& resou return buildMenuItem(VST_MENU_ITEM_ID, VST_MENU_ITEM_ID, - m_currentInputParams.resourceMeta.type == AudioResourceType::VstPlugin, + m_currentInputParams.resourceMeta.type == "VstPlugin", subItemsByType, /*includeInFilteredLists*/ false); } @@ -391,7 +393,7 @@ QVariantMap InputResourceItem::buildSoundFontsMenuItem(const ResourceByVendorMap return buildMenuItem(SOUNDFONTS_MENU_ITEM_ID, muse::qtrc("playback", "SoundFonts"), - m_currentInputParams.resourceMeta.type == AudioResourceType::FluidSoundfont, + m_currentInputParams.resourceMeta.type == "FluidSoundfont", soundFontItems, /*includeInFilteredLists*/ false); } @@ -436,7 +438,8 @@ QVariantMap InputResourceItem::buildMsBasicMenuItem(const AudioResourceMetaList& presetName = muse::qtrc("playback", "Bank %1, preset %2").arg(item.preset.bank).arg(item.preset.program); } - return buildMenuItem(makeMenuResourceItemId(resourceMeta.type, QString::fromStdString(resourceMeta.id)), + return buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(resourceMeta.type), + QString::fromStdString(resourceMeta.id)), presetName, isCurrent); } @@ -487,7 +490,8 @@ QVariantMap InputResourceItem::buildMsBasicMenuItem(const AudioResourceMetaList& // Prepend the "Choose automatically" item categoryItems.prepend(buildSeparator()); - categoryItems.prepend(buildMenuItem(makeMenuResourceItemId(chooseAutomaticMeta.type, QString::fromStdString(chooseAutomaticMeta.id)), + categoryItems.prepend(buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(chooseAutomaticMeta.type), + QString::fromStdString(chooseAutomaticMeta.id)), muse::qtrc("playback", "Choose automatically"), isCurrentSoundFont && !currentPreset.has_value(), /*subItems*/ QVariantList(), @@ -552,7 +556,8 @@ QVariantMap InputResourceItem::buildSoundFontMenuItem(const muse::String& soundF // Prepend the "Choose automatically" item bankItems.prepend(buildSeparator()); - bankItems.prepend(buildMenuItem(makeMenuResourceItemId(chooseAutomaticMeta.type, QString::fromStdString(chooseAutomaticMeta.id)), + bankItems.prepend(buildMenuItem(makeMenuResourceItemId(muse::audio::resourceTypeFromString(chooseAutomaticMeta.type), + QString::fromStdString(chooseAutomaticMeta.id)), muse::qtrc("playback", "Choose automatically"), isCurrentSoundFont && !currentPreset.has_value(), /*subItems*/ QVariantList(), @@ -571,7 +576,7 @@ void InputResourceItem::updateAvailableResources(const AudioResourceMetaList& av m_availableResourceMap.clear(); for (const AudioResourceMeta& meta : availableResources) { - ResourceByVendorMap& resourcesByVendor = m_availableResourceMap[meta.type]; + ResourceByVendorMap& resourcesByVendor = m_availableResourceMap[muse::audio::resourceTypeFromString(meta.type)]; AudioResourceMetaList& resourcesMetaList = resourcesByVendor[meta.vendor]; resourcesMetaList.push_back(meta); } diff --git a/src/playback/qml/MuseScore/Playback/outputresourceitem.cpp b/src/playback/qml/MuseScore/Playback/outputresourceitem.cpp index 7b97dd0121341..9518e939c34e8 100644 --- a/src/playback/qml/MuseScore/Playback/outputresourceitem.cpp +++ b/src/playback/qml/MuseScore/Playback/outputresourceitem.cpp @@ -3,6 +3,7 @@ #include #include "audio/common/audioutils.h" +#include "vst/vstpluginattrs.h" #include "log.h" #include "translation.h" @@ -183,7 +184,7 @@ void OutputResourceItem::updateCurrentFxParams(const AudioResourceMeta& newMeta) requestToCloseNativeEditorView(); audio::AudioFxParams newParams = m_currentFxParams; - newParams.categories = audio::audioFxCategoriesFromString(newMeta.attributeVal(audio::CATEGORIES_ATTRIBUTE)); + newParams.categories = audio::audioFxCategoriesFromString(newMeta.attributeVal(vst::CATEGORIES_ATTRIBUTE)); newParams.resourceMeta = newMeta; newParams.configuration.clear(); newParams.active = newMeta.isValid(); @@ -213,5 +214,5 @@ bool OutputResourceItem::isBlank() const bool OutputResourceItem::hasNativeEditorSupport() const { - return m_currentFxParams.resourceMeta.hasNativeEditorSupport; + return muse::audio::hasNativeEditorSupport(m_currentFxParams.resourceMeta); } diff --git a/src/project/internal/projectaudiosettings.cpp b/src/project/internal/projectaudiosettings.cpp index fb33b56003a20..28789b25227db 100644 --- a/src/project/internal/projectaudiosettings.cpp +++ b/src/project/internal/projectaudiosettings.cpp @@ -29,6 +29,7 @@ #include "types/bytearray.h" #include "audio/common/audioutils.h" +#include "vst/vstpluginattrs.h" using namespace mu::project; using namespace muse; @@ -45,7 +46,7 @@ static const std::map SOURCE_TYPE_MAP = { static void doCompatibilityConversions(AudioResourceMeta& meta) { - if (meta.type == AudioResourceType::MuseSamplerSoundPack) { + if (meta.type == "MuseSamplerSoundPack") { // MS 4.5: resource name and category have been excluded from ID // Old format: category\\name\\uid if (meta.id.find("\\") == std::string::npos) { @@ -380,7 +381,7 @@ AudioFxParams ProjectAudioSettings::fxParamsFromJson(const QJsonObject& object) result.chainOrder = static_cast(object.value("chainOrder").toInt()); result.resourceMeta = resourceMetaFromJson(object.value("resourceMeta").toObject()); result.configuration = unitConfigFromJson(object.value("unitConfiguration").toObject()); - result.categories = audioFxCategoriesFromString(result.resourceMeta.attributeVal(audio::CATEGORIES_ATTRIBUTE)); + result.categories = audioFxCategoriesFromString(result.resourceMeta.attributeVal(vst::CATEGORIES_ATTRIBUTE)); return result; } @@ -410,11 +411,17 @@ AudioResourceMeta ProjectAudioSettings::resourceMetaFromJson(const QJsonObject& { AudioResourceMeta result; result.id = object.value("id").toString().toStdString(); - result.hasNativeEditorSupport = object.value("hasNativeEditorSupport").toBool(); result.vendor = object.value("vendor").toString().toStdString(); result.type = resourceTypeFromString(object.value("type").toString()); result.attributes = attributesFromJson(object.value("attributes").toObject()); + // Migrate the legacy top-level "hasNativeEditorSupport" boolean into attributes + // for project files written before audio plugin cache version 1. + if (object.contains("hasNativeEditorSupport") && !result.attributes.count(audio::HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE)) { + const bool b = object.value("hasNativeEditorSupport").toBool(); + result.attributes.emplace(audio::HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE, b ? u"true" : u"false"); + } + return result; } @@ -519,7 +526,6 @@ QJsonObject ProjectAudioSettings::resourceMetaToJson(const AudioResourceMeta& me { QJsonObject result; result.insert("id", QString::fromStdString(meta.id)); - result.insert("hasNativeEditorSupport", meta.hasNativeEditorSupport); result.insert("vendor", QString::fromStdString(meta.vendor)); result.insert("type", audioResourceTypeToString(meta.type).toQString()); result.insert("attributes", attributesToJson(meta.attributes)); @@ -561,15 +567,15 @@ AudioSourceType ProjectAudioSettings::sourceTypeFromString(const QString& string return AudioSourceType::Undefined; } -AudioResourceType ProjectAudioSettings::resourceTypeFromString(const QString& string) const +audioplugins::AudioResourceType ProjectAudioSettings::resourceTypeFromString(const QString& string) const { for (const auto& pair : RESOURCE_TYPE_MAP) { if (pair.second == string) { - return pair.first; + return audio::resourceTypeName(pair.first); } } - return AudioResourceType::Undefined; + return audioplugins::AudioResourceType(); } QString ProjectAudioSettings::sourceTypeToString(const AudioSourceType& type) const diff --git a/src/project/internal/projectaudiosettings.h b/src/project/internal/projectaudiosettings.h index eda61bf16ed54..81c89e3f0a36d 100644 --- a/src/project/internal/projectaudiosettings.h +++ b/src/project/internal/projectaudiosettings.h @@ -101,7 +101,7 @@ class ProjectAudioSettings : public IProjectAudioSettings, public muse::Contexta QJsonObject attributesToJson(const muse::audio::AudioResourceAttributes& attributes) const; muse::audio::AudioSourceType sourceTypeFromString(const QString& string) const; - muse::audio::AudioResourceType resourceTypeFromString(const QString& string) const; + muse::audioplugins::AudioResourceType resourceTypeFromString(const QString& string) const; QString sourceTypeToString(const muse::audio::AudioSourceType& type) const; From 90b9fb8d949bf7b4ac613333c10e0ae21148e022 Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Mon, 11 May 2026 15:38:56 +0200 Subject: [PATCH 03/10] [playback,project,notationscene] use audio::isResourceType helper Replaces the remaining literal-string plugin-type comparisons (e.g. meta.type == "MuseSamplerSoundPack") with audio::isResourceType(meta, AudioResourceType::X). Single source of truth for the wire strings now lives in the framework enum's bridge, plus a unit test pinning them. Bumps muse/ to pick up the framework changes. --- .../percussionpanel/percussionpanelmodel.cpp | 3 ++- src/playback/internal/drumsetloader.cpp | 2 +- src/playback/internal/playbackcontroller.cpp | 3 ++- src/playback/internal/soundprofilesrepository.cpp | 4 ++-- .../qml/MuseScore/Playback/inputresourceitem.cpp | 9 ++++++--- src/project/internal/projectaudiosettings.cpp | 2 +- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp b/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp index c81936c4755a6..7595121e027ff 100644 --- a/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp +++ b/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp @@ -553,7 +553,8 @@ void PercussionPanelModel::resetLayout() } const muse::audio::AudioResourceMeta& resourceMeta = audioSettings()->trackInputParams(currentTrackId()).resourceMeta; - const bool isMuseSamplerDrumset = resourceMeta.type == "MuseSamplerSoundPack"; + const bool isMuseSamplerDrumset = muse::audio::isResourceType(resourceMeta, + muse::audio::AudioResourceType::MuseSamplerSoundPack); Drumset defaultDrumset = isMuseSamplerDrumset ? museSamplerDefaultDrumset() : standardDefaultDrumset(); diff --git a/src/playback/internal/drumsetloader.cpp b/src/playback/internal/drumsetloader.cpp index c94d1c2c314ce..b249a662025e5 100644 --- a/src/playback/internal/drumsetloader.cpp +++ b/src/playback/internal/drumsetloader.cpp @@ -41,7 +41,7 @@ void DrumsetLoader::loadDrumset(INotationPtr notation, const InstrumentTrackId& } // restore the default drumset when changing from MuseSounds to MS Basic / VST - if (resourceMeta.type != "MuseSamplerSoundPack") { + if (!isResourceType(resourceMeta, AudioResourceType::MuseSamplerSoundPack)) { const InstrumentTemplate& templ = instrumentsRepository()->instrumentTemplate(trackId.instrumentId); if (!templ.useDrumset) { return; diff --git a/src/playback/internal/playbackcontroller.cpp b/src/playback/internal/playbackcontroller.cpp index 26198d701a5b9..90fc311629bd4 100644 --- a/src/playback/internal/playbackcontroller.cpp +++ b/src/playback/internal/playbackcontroller.cpp @@ -493,7 +493,8 @@ bool PlaybackController::shouldLoadDrumset(const engraving::InstrumentTrackId& i return false; } - return oldMeta.type == "MuseSamplerSoundPack" || newMeta.type == "MuseSamplerSoundPack"; + return isResourceType(oldMeta, AudioResourceType::MuseSamplerSoundPack) + || isResourceType(newMeta, AudioResourceType::MuseSamplerSoundPack); } void PlaybackController::addSoundFlagsIfNeed(const std::vector& selection) diff --git a/src/playback/internal/soundprofilesrepository.cpp b/src/playback/internal/soundprofilesrepository.cpp index 25a96823ce63a..890793692e21c 100644 --- a/src/playback/internal/soundprofilesrepository.cpp +++ b/src/playback/internal/soundprofilesrepository.cpp @@ -67,11 +67,11 @@ void SoundProfilesRepository::refresh() continue; } - if (resource.type == "FluidSoundfont") { + if (isResourceType(resource, AudioResourceType::FluidSoundfont)) { basicProfile.data.emplace(mpe::PlaybackSetupData::fromString(setup->second), resource); } - if (resource.type == "MuseSamplerSoundPack") { + if (isResourceType(resource, AudioResourceType::MuseSamplerSoundPack)) { museProfile.data.emplace(mpe::PlaybackSetupData::fromString(setup->second), resource); } } diff --git a/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp b/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp index ff0f24c034153..2c73bb53d60d8 100644 --- a/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp +++ b/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp @@ -302,7 +302,8 @@ QVariantMap InputResourceItem::buildMuseMenuItem(const ResourceByVendorMap& reso return buildMenuItem(MUSE_MENU_ITEM_ID, MUSE_MENU_ITEM_ID, - m_currentInputParams.resourceMeta.type == "MuseSamplerSoundPack", + muse::audio::isResourceType(m_currentInputParams.resourceMeta, + muse::audio::AudioResourceType::MuseSamplerSoundPack), subItemsByType, /*includeInFilteredLists*/ false); } @@ -332,7 +333,8 @@ QVariantMap InputResourceItem::buildVstMenuItem(const ResourceByVendorMap& resou return buildMenuItem(VST_MENU_ITEM_ID, VST_MENU_ITEM_ID, - m_currentInputParams.resourceMeta.type == "VstPlugin", + muse::audio::isResourceType(m_currentInputParams.resourceMeta, + muse::audio::AudioResourceType::VstPlugin), subItemsByType, /*includeInFilteredLists*/ false); } @@ -393,7 +395,8 @@ QVariantMap InputResourceItem::buildSoundFontsMenuItem(const ResourceByVendorMap return buildMenuItem(SOUNDFONTS_MENU_ITEM_ID, muse::qtrc("playback", "SoundFonts"), - m_currentInputParams.resourceMeta.type == "FluidSoundfont", + muse::audio::isResourceType(m_currentInputParams.resourceMeta, + muse::audio::AudioResourceType::FluidSoundfont), soundFontItems, /*includeInFilteredLists*/ false); } diff --git a/src/project/internal/projectaudiosettings.cpp b/src/project/internal/projectaudiosettings.cpp index 28789b25227db..86b098660861e 100644 --- a/src/project/internal/projectaudiosettings.cpp +++ b/src/project/internal/projectaudiosettings.cpp @@ -46,7 +46,7 @@ static const std::map SOURCE_TYPE_MAP = { static void doCompatibilityConversions(AudioResourceMeta& meta) { - if (meta.type == "MuseSamplerSoundPack") { + if (isResourceType(meta, AudioResourceType::MuseSamplerSoundPack)) { // MS 4.5: resource name and category have been excluded from ID // Old format: category\\name\\uid if (meta.id.find("\\") == std::string::npos) { From b6bc86db7a2b2f46de0dfd95bfffe73ba1db81a7 Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Wed, 13 May 2026 12:50:24 +0200 Subject: [PATCH 04/10] [audioplugins] framework auto-registers its own cache migrations KnownAudioPluginsMigrationRegister's constructor now pre-registers the framework-owned migrations so apps don't have to copy-paste them: v0 -> v1: structural (envelope intro, no-op callback) v1 -> v2: enabled boolean -> state string (AudioPluginState is a framework enum, so this transformation belongs here) Apps register only their own field migrations on top. Bumps CURRENT_KNOWN_AUDIO_PLUGINS_VERSION 2 -> 3 since the MuseScore-specific hasNativeEditorSupport migration moves to v2 -> v3 (app-owned). --- .../internal/audiopluginsappconfigmodule.cpp | 40 ++++++------------- src/project/internal/projectaudiosettings.cpp | 5 ++- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/app/internal/audiopluginsappconfigmodule.cpp b/src/app/internal/audiopluginsappconfigmodule.cpp index 40c90068bb352..810407c101571 100644 --- a/src/app/internal/audiopluginsappconfigmodule.cpp +++ b/src/app/internal/audiopluginsappconfigmodule.cpp @@ -54,28 +54,33 @@ void AudioPluginsAppConfigModule::resolveImports() configuration->setRuntimeAttributeDefaults(runtimeDefaults); } + // MuseScore-specific audioplugins migrations only — framework-owned + // steps (v0->v1 structural, v1->v2 enabled->state) are pre-registered + // by the framework's AudioPluginsModule. auto migrations = muse::modularity::globalIoc()->resolve(moduleName()); if (migrations) { - // v0 → v1: hasNativeEditorSupport moved from a top-level meta field - // into meta.attributes (string "true"/"false"). - migrations->registerMigration(0, [](const muse::JsonArray& plugins) { + // v2 → v3: hasNativeEditorSupport moved from a top-level meta field + // into meta.attributes (string "true"/"false"). MuseScore-specific: + // audacity has no native-editor concept. + migrations->registerMigration(2, [](const muse::JsonArray& plugins) { + const std::string nativeEditorKey = muse::audio::HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE.toStdString(); muse::JsonArray out; for (size_t i = 0; i < plugins.size(); ++i) { muse::JsonObject obj = plugins.at(i).toObject(); muse::JsonObject meta = obj.value("meta").toObject(); - if (meta.contains("hasNativeEditorSupport")) { + if (meta.contains(nativeEditorKey)) { muse::JsonObject attrs; if (meta.contains("attributes")) { attrs = meta.value("attributes").toObject(); } - const bool b = meta.value("hasNativeEditorSupport").toBool(); - attrs.set("hasNativeEditorSupport", b ? std::string("true") : std::string("false")); + const bool b = meta.value(nativeEditorKey).toBool(); + attrs.set(nativeEditorKey, b ? std::string("true") : std::string("false")); meta.set("attributes", attrs); // JsonObject has no remove(); rebuild without the legacy key. muse::JsonObject metaWithoutLegacy; for (const std::string& k : meta.keys()) { - if (k == "hasNativeEditorSupport") { + if (k == nativeEditorKey) { continue; } metaWithoutLegacy.set(k, meta.value(k)); @@ -86,26 +91,5 @@ void AudioPluginsAppConfigModule::resolveImports() } return out; }); - - // v1 → v2: replace the boolean `enabled` flag with a `state` string - // ("Validated" / "Error"). errorCode is preserved. - migrations->registerMigration(1, [](const muse::JsonArray& plugins) { - muse::JsonArray out; - for (size_t i = 0; i < plugins.size(); ++i) { - muse::JsonObject obj = plugins.at(i).toObject(); - const bool enabled = obj.value("enabled").toBool(); - obj.set("state", enabled ? std::string("Validated") : std::string("Error")); - - muse::JsonObject rebuilt; - for (const std::string& k : obj.keys()) { - if (k == "enabled") { - continue; - } - rebuilt.set(k, obj.value(k)); - } - out << rebuilt; - } - return out; - }); } } diff --git a/src/project/internal/projectaudiosettings.cpp b/src/project/internal/projectaudiosettings.cpp index 86b098660861e..d30dbfe2aeec0 100644 --- a/src/project/internal/projectaudiosettings.cpp +++ b/src/project/internal/projectaudiosettings.cpp @@ -417,8 +417,9 @@ AudioResourceMeta ProjectAudioSettings::resourceMetaFromJson(const QJsonObject& // Migrate the legacy top-level "hasNativeEditorSupport" boolean into attributes // for project files written before audio plugin cache version 1. - if (object.contains("hasNativeEditorSupport") && !result.attributes.count(audio::HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE)) { - const bool b = object.value("hasNativeEditorSupport").toBool(); + const QString nativeEditorKey = audio::HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE.toQString(); + if (object.contains(nativeEditorKey) && !result.attributes.count(audio::HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE)) { + const bool b = object.value(nativeEditorKey).toBool(); result.attributes.emplace(audio::HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE, b ? u"true" : u"false"); } From ba3d33db6f430692cd09b0701016eab4f853801d Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Thu, 28 May 2026 08:03:05 +0200 Subject: [PATCH 05/10] [playback] retype menu-parsed ids as audioplugins::PluginResourceId inputresourceitem and outputresourceitem parse a plugin id out of the menu-item string the UI emits, then use it to look up plugin metadata. That value is plugin identity, not an audio-pipeline resource id; type it accordingly now that the framework distinguishes them. Three local sites only; everything else in src/ reaches the id via resourceMeta.id (struct field access, type-resolved automatically). --- src/playback/qml/MuseScore/Playback/inputresourceitem.cpp | 4 ++-- src/playback/qml/MuseScore/Playback/outputresourceitem.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp b/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp index 2c73bb53d60d8..cdf2784c4cc49 100644 --- a/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp +++ b/src/playback/qml/MuseScore/Playback/inputresourceitem.cpp @@ -64,7 +64,7 @@ static QString makeMenuResourceItemId(AudioResourceType type, const QString& res return str + "\\" + resourceId; } -static void parseAudioResourceTypeAndId(const QString& menuItemId, AudioResourceType& type, AudioResourceId& resourceId) +static void parseAudioResourceTypeAndId(const QString& menuItemId, AudioResourceType& type, audioplugins::PluginResourceId& resourceId) { QString typeStr = menuItemId.section("\\", 0, 0); type = muse::key(AUDIO_RESOURCE_TYPE_TO_STR, typeStr); @@ -139,7 +139,7 @@ void InputResourceItem::handleMenuItem(const QString& menuItemId) } AudioResourceType newResourceType = AudioResourceType::Undefined; - AudioResourceId newResourceId; + audioplugins::PluginResourceId newResourceId; parseAudioResourceTypeAndId(menuItemId, newResourceType, newResourceId); auto resourcesIt = m_availableResourceMap.find(newResourceType); diff --git a/src/playback/qml/MuseScore/Playback/outputresourceitem.cpp b/src/playback/qml/MuseScore/Playback/outputresourceitem.cpp index 9518e939c34e8..977880afed9d6 100644 --- a/src/playback/qml/MuseScore/Playback/outputresourceitem.cpp +++ b/src/playback/qml/MuseScore/Playback/outputresourceitem.cpp @@ -104,7 +104,7 @@ void OutputResourceItem::handleMenuItem(const QString& menuItemId) return; } - const AudioResourceId& newSelectedResourceId = menuItemId.toStdString(); + const audioplugins::PluginResourceId& newSelectedResourceId = menuItemId.toStdString(); for (const auto& pair : m_fxByVendorMap) { for (const AudioResourceMeta& fxResourceMeta : pair.second) { From 4fdf3a66292c4f816cacfc811cc4430a3deafbb0 Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Fri, 29 May 2026 13:58:53 +0200 Subject: [PATCH 06/10] [playback] add audio<->plugin meta bridge New file pair src/playback/internal/audiometabridge.{h,cpp} with toAudioMeta / toPluginMeta plus list variants. Converts field-wise between audio::AudioResourceMeta (engine-domain) and audioplugins:: PluginMeta (cache-domain) - the two struct types the framework now keeps independent. No call sites changed yet; the next commit retypes all meta usage in MuseScore (rename audioplugins::AudioResourceMeta references to audioplugins::PluginMeta, insert bridge calls at the natural seams in playbackcontroller, projectaudiosettings, soundprofilesrepository). --- src/playback/CMakeLists.txt | 2 + src/playback/internal/audiometabridge.cpp | 65 +++++++++++++++++++++++ src/playback/internal/audiometabridge.h | 39 ++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 src/playback/internal/audiometabridge.cpp create mode 100644 src/playback/internal/audiometabridge.h diff --git a/src/playback/CMakeLists.txt b/src/playback/CMakeLists.txt index cb7e49ea53f35..8699f364f8793 100644 --- a/src/playback/CMakeLists.txt +++ b/src/playback/CMakeLists.txt @@ -42,6 +42,8 @@ target_sources(playback PRIVATE internal/soundprofilesrepository.h internal/drumsetloader.cpp internal/drumsetloader.h + internal/audiometabridge.cpp + internal/audiometabridge.h ) if (MUE_BUILD_PLAYBACK_TESTS) diff --git a/src/playback/internal/audiometabridge.cpp b/src/playback/internal/audiometabridge.cpp new file mode 100644 index 0000000000000..cebb66aa1c037 --- /dev/null +++ b/src/playback/internal/audiometabridge.cpp @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2026 MuseScore Limited and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "audiometabridge.h" + +namespace mu::playback { +muse::audio::AudioResourceMeta toAudioMeta(const muse::audioplugins::PluginMeta& meta) +{ + muse::audio::AudioResourceMeta out; + out.id = meta.id; + out.vendor = meta.vendor; + out.attributes = meta.attributes; + out.type = meta.type; + return out; +} + +muse::audioplugins::PluginMeta toPluginMeta(const muse::audio::AudioResourceMeta& meta) +{ + muse::audioplugins::PluginMeta out; + out.id = meta.id; + out.vendor = meta.vendor; + out.attributes = meta.attributes; + out.type = meta.type; + return out; +} + +muse::audio::AudioResourceMetaList toAudioMetaList(const muse::audioplugins::PluginMetaList& metas) +{ + muse::audio::AudioResourceMetaList out; + out.reserve(metas.size()); + for (const auto& meta : metas) { + out.push_back(toAudioMeta(meta)); + } + return out; +} + +muse::audioplugins::PluginMetaList toPluginMetaList(const muse::audio::AudioResourceMetaList& metas) +{ + muse::audioplugins::PluginMetaList out; + out.reserve(metas.size()); + for (const auto& meta : metas) { + out.push_back(toPluginMeta(meta)); + } + return out; +} +} diff --git a/src/playback/internal/audiometabridge.h b/src/playback/internal/audiometabridge.h new file mode 100644 index 0000000000000..a3cf031768e33 --- /dev/null +++ b/src/playback/internal/audiometabridge.h @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2026 MuseScore Limited and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "audio/common/audiotypes.h" +#include "audioplugins/audiopluginstypes.h" + +// App-side bridge between audio::AudioResourceMeta (engine-domain) and +// audioplugins::PluginMeta (cache-domain). The framework keeps the two +// modules independent at the type/interface level; this is where MuseScore +// converts between them at the natural seam - the playback controller, the +// project audio settings, the sound profiles repository, etc. +namespace mu::playback { +muse::audio::AudioResourceMeta toAudioMeta(const muse::audioplugins::PluginMeta& meta); +muse::audioplugins::PluginMeta toPluginMeta(const muse::audio::AudioResourceMeta& meta); + +muse::audio::AudioResourceMetaList toAudioMetaList(const muse::audioplugins::PluginMetaList& metas); +muse::audioplugins::PluginMetaList toPluginMetaList(const muse::audio::AudioResourceMetaList& metas); +} From e68a5f9ec9969ce7134c0649c546e5b0684866d4 Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Fri, 29 May 2026 17:12:28 +0200 Subject: [PATCH 07/10] [app,playback,project,notationscene] adapt to framework audio<->audioplugins split After the framework decoupling: audio::AudioResourceMeta and audioplugins::PluginMeta are distinct struct types. MuseScore code that handles meta in the audio domain keeps using AudioResourceMeta (unqualified, resolves to audio); the few sites that called audioplugins::*Attribute helpers on audio-side meta now use the audio:: local helpers (audio gained its own boolAttribute / intAttribute in the same framework commit). - drumsetloader and percussionpanelmodel switch from audioplugins::intAttribute(meta, ...) to audio::intAttribute(...) since the meta they read is audio-domain (received from playback controller / audio settings). - projectaudiosettings retypes the qualified audioplugins:: AudioResourceType to its new spelling audioplugins::PluginType. - audiopluginsappconfigmodule constructs PluginAttributes (the audioplugins runtime-defaults map) using the renamed type name. The new audiometabridge from the previous commit is in place for future use when the two struct types diverge; no MuseScore call site needs it right now because the app keeps meta within one domain at a time. --- src/app/internal/audiopluginsappconfigmodule.cpp | 2 +- .../NotationScene/percussionpanel/percussionpanelmodel.cpp | 2 +- src/playback/internal/drumsetloader.cpp | 2 +- src/project/internal/projectaudiosettings.cpp | 4 ++-- src/project/internal/projectaudiosettings.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/internal/audiopluginsappconfigmodule.cpp b/src/app/internal/audiopluginsappconfigmodule.cpp index 810407c101571..84bcd9ed412a2 100644 --- a/src/app/internal/audiopluginsappconfigmodule.cpp +++ b/src/app/internal/audiopluginsappconfigmodule.cpp @@ -48,7 +48,7 @@ void AudioPluginsAppConfigModule::resolveImports() // MuseScore's audio engine routes synthesis via the playbackSetupData // attribute. It is runtime-only — the engine re-injects the generic // default at every load, so the cache file should not persist it. - AudioResourceAttributes runtimeDefaults; + PluginAttributes runtimeDefaults; runtimeDefaults.emplace(muse::audio::PLAYBACK_SETUP_DATA_ATTRIBUTE, muse::mpe::GENERIC_SETUP_DATA_STRING); configuration->setRuntimeAttributeDefaults(runtimeDefaults); diff --git a/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp b/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp index 7595121e027ff..3f0acffb7a620 100644 --- a/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp +++ b/src/notationscene/qml/MuseScore/NotationScene/percussionpanel/percussionpanelmodel.cpp @@ -594,7 +594,7 @@ Drumset PercussionPanelModel::museSamplerDefaultDrumset() const const muse::audio::AudioResourceMeta& resourceMeta = audioSettings()->trackInputParams(currentTrackId()).resourceMeta; - const int instrumentId = muse::audioplugins::intAttribute(resourceMeta, u"museUID"); + const int instrumentId = muse::audio::intAttribute(resourceMeta, u"museUID"); const muse::ByteArray drumMapping = museSampler()->drumMapping(instrumentId); IF_ASSERT_FAILED(!drumMapping.empty()) { diff --git a/src/playback/internal/drumsetloader.cpp b/src/playback/internal/drumsetloader.cpp index b249a662025e5..b5dd2a140d226 100644 --- a/src/playback/internal/drumsetloader.cpp +++ b/src/playback/internal/drumsetloader.cpp @@ -56,7 +56,7 @@ void DrumsetLoader::loadDrumset(INotationPtr notation, const InstrumentTrackId& return; } - int instrumentId = muse::audioplugins::intAttribute(resourceMeta, u"museUID"); + int instrumentId = muse::audio::intAttribute(resourceMeta, u"museUID"); auto it = m_drumsetCache.find(instrumentId); if (it != m_drumsetCache.end()) { diff --git a/src/project/internal/projectaudiosettings.cpp b/src/project/internal/projectaudiosettings.cpp index d30dbfe2aeec0..a2d4dd09adac1 100644 --- a/src/project/internal/projectaudiosettings.cpp +++ b/src/project/internal/projectaudiosettings.cpp @@ -568,7 +568,7 @@ AudioSourceType ProjectAudioSettings::sourceTypeFromString(const QString& string return AudioSourceType::Undefined; } -audioplugins::AudioResourceType ProjectAudioSettings::resourceTypeFromString(const QString& string) const +audioplugins::PluginType ProjectAudioSettings::resourceTypeFromString(const QString& string) const { for (const auto& pair : RESOURCE_TYPE_MAP) { if (pair.second == string) { @@ -576,7 +576,7 @@ audioplugins::AudioResourceType ProjectAudioSettings::resourceTypeFromString(con } } - return audioplugins::AudioResourceType(); + return audioplugins::PluginType(); } QString ProjectAudioSettings::sourceTypeToString(const AudioSourceType& type) const diff --git a/src/project/internal/projectaudiosettings.h b/src/project/internal/projectaudiosettings.h index 81c89e3f0a36d..0c2be1b0fa898 100644 --- a/src/project/internal/projectaudiosettings.h +++ b/src/project/internal/projectaudiosettings.h @@ -101,7 +101,7 @@ class ProjectAudioSettings : public IProjectAudioSettings, public muse::Contexta QJsonObject attributesToJson(const muse::audio::AudioResourceAttributes& attributes) const; muse::audio::AudioSourceType sourceTypeFromString(const QString& string) const; - muse::audioplugins::AudioResourceType resourceTypeFromString(const QString& string) const; + muse::audioplugins::PluginType resourceTypeFromString(const QString& string) const; QString sourceTypeToString(const muse::audio::AudioSourceType& type) const; From 525108b36e4ac61ac95bc749e0407c86fb6f9fa4 Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Tue, 9 Jun 2026 12:03:13 +0200 Subject: [PATCH 08/10] [playback] replace AudioPluginsAppConfigModule with KnownAudioPluginsConfigurator Per reviewer feedback: AudioPluginsAppConfigModule was an IModuleSetup registered alongside real modules but only did a little app-specific wiring - too insignificant to be a module. Replace it with a plain KnownAudioPluginsConfigurator class owned by PlaybackModule and invoked from PlaybackModule::resolveImports() (same lifecycle phase, before the cache loads). Both responsibilities are playback-domain: the runtime playbackSetupData default and the hasNativeEditorSupport v2->v3 cache migration. No behavior change. --- src/app/CMakeLists.txt | 3 --- src/app/appfactory.cpp | 4 ---- src/playback/CMakeLists.txt | 2 ++ .../internal/knownaudiopluginsconfigurator.cpp} | 17 +++++++---------- .../internal/knownaudiopluginsconfigurator.h} | 13 +++++++------ src/playback/playbackmodule.cpp | 6 ++++++ 6 files changed, 22 insertions(+), 23 deletions(-) rename src/{app/internal/audiopluginsappconfigmodule.cpp => playback/internal/knownaudiopluginsconfigurator.cpp} (91%) rename src/{app/internal/audiopluginsappconfigmodule.h => playback/internal/knownaudiopluginsconfigurator.h} (67%) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index c63c0340ba2d0..fe5bd214e39cc 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -217,9 +217,6 @@ set(APP_SRC appfactory.cpp appfactory.h - internal/audiopluginsappconfigmodule.cpp - internal/audiopluginsappconfigmodule.h - internal/guiapp.cpp internal/guiapp.h ) diff --git a/src/app/appfactory.cpp b/src/app/appfactory.cpp index 8c5c027cec21a..fff9a369d9225 100644 --- a/src/app/appfactory.cpp +++ b/src/app/appfactory.cpp @@ -26,7 +26,6 @@ #ifdef MUSE_MODULE_AUDIOPLUGINS #include "framework/audioplugins/audiopluginsmodule.h" -#include "internal/audiopluginsappconfigmodule.h" #endif #ifdef MUSE_MODULE_CLOUD @@ -319,7 +318,6 @@ std::shared_ptr AppFactory::newGuiApp(const std::shared_ptr< app->addModule(new muse::audio::AudioModule()); #ifdef MUSE_MODULE_AUDIOPLUGINS app->addModule(new muse::audioplugins::AudioPluginsModule()); - app->addModule(new mu::app::AudioPluginsAppConfigModule()); #endif app->addModule(new muse::automation::AutomationModule()); app->addModule(new muse::draw::DrawModule()); @@ -463,7 +461,6 @@ static void addConsoleModules(std::shared_ptr app) app->addModule(new muse::audio::AudioModule()); #ifdef MUSE_MODULE_AUDIOPLUGINS app->addModule(new muse::audioplugins::AudioPluginsModule()); - app->addModule(new mu::app::AudioPluginsAppConfigModule()); #endif app->addModule(new muse::draw::DrawModule()); app->addModule(new muse::midi::MidiModule()); @@ -556,7 +553,6 @@ static void addAudioPluginRegistrationModules(std::shared_ptraddModule(new muse::audioplugins::AudioPluginsModule()); - app->addModule(new mu::app::AudioPluginsAppConfigModule()); #endif #ifdef MUSE_MODULE_VST diff --git a/src/playback/CMakeLists.txt b/src/playback/CMakeLists.txt index 8699f364f8793..89a07db2db8f2 100644 --- a/src/playback/CMakeLists.txt +++ b/src/playback/CMakeLists.txt @@ -44,6 +44,8 @@ target_sources(playback PRIVATE internal/drumsetloader.h internal/audiometabridge.cpp internal/audiometabridge.h + internal/knownaudiopluginsconfigurator.cpp + internal/knownaudiopluginsconfigurator.h ) if (MUE_BUILD_PLAYBACK_TESTS) diff --git a/src/app/internal/audiopluginsappconfigmodule.cpp b/src/playback/internal/knownaudiopluginsconfigurator.cpp similarity index 91% rename from src/app/internal/audiopluginsappconfigmodule.cpp rename to src/playback/internal/knownaudiopluginsconfigurator.cpp index 84bcd9ed412a2..bca602c213387 100644 --- a/src/app/internal/audiopluginsappconfigmodule.cpp +++ b/src/playback/internal/knownaudiopluginsconfigurator.cpp @@ -19,7 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "audiopluginsappconfigmodule.h" +#include "knownaudiopluginsconfigurator.h" #include "modularity/ioc.h" @@ -31,19 +31,16 @@ #include "audioplugins/iaudiopluginsconfiguration.h" #include "audioplugins/iknownaudiopluginsmigrationregister.h" -using namespace mu::app; +using namespace mu::playback; using namespace muse::audioplugins; -static const std::string mname("audiopluginsappconfig"); - -std::string AudioPluginsAppConfigModule::moduleName() const -{ - return mname; +namespace { +const std::string CONFIGURATOR_NAME("knownaudiopluginsconfigurator"); } -void AudioPluginsAppConfigModule::resolveImports() +void KnownAudioPluginsConfigurator::init() { - auto configuration = muse::modularity::globalIoc()->resolve(moduleName()); + auto configuration = muse::modularity::globalIoc()->resolve(CONFIGURATOR_NAME); if (configuration) { // MuseScore's audio engine routes synthesis via the playbackSetupData // attribute. It is runtime-only — the engine re-injects the generic @@ -57,7 +54,7 @@ void AudioPluginsAppConfigModule::resolveImports() // MuseScore-specific audioplugins migrations only — framework-owned // steps (v0->v1 structural, v1->v2 enabled->state) are pre-registered // by the framework's AudioPluginsModule. - auto migrations = muse::modularity::globalIoc()->resolve(moduleName()); + auto migrations = muse::modularity::globalIoc()->resolve(CONFIGURATOR_NAME); if (migrations) { // v2 → v3: hasNativeEditorSupport moved from a top-level meta field // into meta.attributes (string "true"/"false"). MuseScore-specific: diff --git a/src/app/internal/audiopluginsappconfigmodule.h b/src/playback/internal/knownaudiopluginsconfigurator.h similarity index 67% rename from src/app/internal/audiopluginsappconfigmodule.h rename to src/playback/internal/knownaudiopluginsconfigurator.h index c5c959f81adb6..bae17aa78e394 100644 --- a/src/app/internal/audiopluginsappconfigmodule.h +++ b/src/playback/internal/knownaudiopluginsconfigurator.h @@ -21,13 +21,14 @@ */ #pragma once -#include "modularity/imodulesetup.h" - -namespace mu::app { -class AudioPluginsAppConfigModule : public muse::modularity::IModuleSetup +namespace mu::playback { +// MuseScore-specific configuration of the framework's shared known-audio-plugins +// cache: marks runtime-only attributes and registers the app's cache migration. +// Not a module — it's one-shot setup invoked from PlaybackModule::resolveImports() +// (before the cache is loaded in the audioplugins context onInit). +class KnownAudioPluginsConfigurator { public: - std::string moduleName() const override; - void resolveImports() override; + void init(); }; } diff --git a/src/playback/playbackmodule.cpp b/src/playback/playbackmodule.cpp index 3fc0d6ade5f50..1ea510fc2c06f 100644 --- a/src/playback/playbackmodule.cpp +++ b/src/playback/playbackmodule.cpp @@ -33,6 +33,7 @@ #include "internal/playbackcommands.h" #include "internal/playbackconfiguration.h" #include "internal/soundprofilesrepository.h" +#include "internal/knownaudiopluginsconfigurator.h" using namespace mu::playback; using namespace muse; @@ -64,6 +65,11 @@ void PlaybackModule::resolveImports() if (cr) { cr->reg(std::make_shared()); } + + // MuseScore-specific setup of the shared known-audio-plugins cache. Must run + // before the cache is loaded in the audioplugins context onInit — resolveImports + // of every module precedes any onInit, so this is the right phase. + KnownAudioPluginsConfigurator().init(); } void PlaybackModule::onInit(const IApplication::RunMode&) From 7ea6c7159d004f9f44fd3095c4f58cd400189d48 Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Wed, 10 Jun 2026 14:35:59 +0200 Subject: [PATCH 09/10] [playback] resolve KnownAudioPluginsConfigurator deps via GlobalInject Mirror the Audacity configurator: resolve IAudioPluginsConfiguration and IKnownAudioPluginsMigrationRegister through muse::GlobalInject<> members instead of manual globalIoc()->resolve<...>(name) calls in init(). Matches the idiom already used throughout MuseScore playback (playbackcontroller, soundprofilesrepository, etc.). No behavior change. --- .../knownaudiopluginsconfigurator.cpp | 19 ++++--------------- .../internal/knownaudiopluginsconfigurator.h | 8 ++++++++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/playback/internal/knownaudiopluginsconfigurator.cpp b/src/playback/internal/knownaudiopluginsconfigurator.cpp index bca602c213387..65308f3f231c9 100644 --- a/src/playback/internal/knownaudiopluginsconfigurator.cpp +++ b/src/playback/internal/knownaudiopluginsconfigurator.cpp @@ -21,45 +21,34 @@ */ #include "knownaudiopluginsconfigurator.h" -#include "modularity/ioc.h" - #include "global/serialization/json.h" #include "audio/common/audiotypes.h" #include "mpe/playbacksetupdata.h" -#include "audioplugins/iaudiopluginsconfiguration.h" -#include "audioplugins/iknownaudiopluginsmigrationregister.h" - using namespace mu::playback; using namespace muse::audioplugins; -namespace { -const std::string CONFIGURATOR_NAME("knownaudiopluginsconfigurator"); -} - void KnownAudioPluginsConfigurator::init() { - auto configuration = muse::modularity::globalIoc()->resolve(CONFIGURATOR_NAME); - if (configuration) { + if (m_audioPluginConfiguration()) { // MuseScore's audio engine routes synthesis via the playbackSetupData // attribute. It is runtime-only — the engine re-injects the generic // default at every load, so the cache file should not persist it. PluginAttributes runtimeDefaults; runtimeDefaults.emplace(muse::audio::PLAYBACK_SETUP_DATA_ATTRIBUTE, muse::mpe::GENERIC_SETUP_DATA_STRING); - configuration->setRuntimeAttributeDefaults(runtimeDefaults); + m_audioPluginConfiguration()->setRuntimeAttributeDefaults(runtimeDefaults); } // MuseScore-specific audioplugins migrations only — framework-owned // steps (v0->v1 structural, v1->v2 enabled->state) are pre-registered // by the framework's AudioPluginsModule. - auto migrations = muse::modularity::globalIoc()->resolve(CONFIGURATOR_NAME); - if (migrations) { + if (m_migrationRegister()) { // v2 → v3: hasNativeEditorSupport moved from a top-level meta field // into meta.attributes (string "true"/"false"). MuseScore-specific: // audacity has no native-editor concept. - migrations->registerMigration(2, [](const muse::JsonArray& plugins) { + m_migrationRegister()->registerMigration(2, [](const muse::JsonArray& plugins) { const std::string nativeEditorKey = muse::audio::HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE.toStdString(); muse::JsonArray out; for (size_t i = 0; i < plugins.size(); ++i) { diff --git a/src/playback/internal/knownaudiopluginsconfigurator.h b/src/playback/internal/knownaudiopluginsconfigurator.h index bae17aa78e394..d43e3bd489c17 100644 --- a/src/playback/internal/knownaudiopluginsconfigurator.h +++ b/src/playback/internal/knownaudiopluginsconfigurator.h @@ -21,6 +21,11 @@ */ #pragma once +#include "modularity/ioc.h" + +#include "audioplugins/iaudiopluginsconfiguration.h" +#include "audioplugins/iknownaudiopluginsmigrationregister.h" + namespace mu::playback { // MuseScore-specific configuration of the framework's shared known-audio-plugins // cache: marks runtime-only attributes and registers the app's cache migration. @@ -28,6 +33,9 @@ namespace mu::playback { // (before the cache is loaded in the audioplugins context onInit). class KnownAudioPluginsConfigurator { + muse::GlobalInject m_audioPluginConfiguration; + muse::GlobalInject m_migrationRegister; + public: void init(); }; From 4dc7c8630626ea9f6679b66051e289df1476a19e Mon Sep 17 00:00:00 2001 From: Paul MARTIN Date: Thu, 18 Jun 2026 17:11:12 +0200 Subject: [PATCH 10/10] update framework --- muse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/muse b/muse index b8ca198cc172b..553c5da90153f 160000 --- a/muse +++ b/muse @@ -1 +1 @@ -Subproject commit b8ca198cc172b48ed6a7c4f2f0656f33c2225cc7 +Subproject commit 553c5da90153fa2ac0e51bc02ce2c4e3fe3272a3