Revamp audio plugins module#47
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR replaces enum-based plugin/resource types with string-based audioplugins::AudioResourceType and AudioResourceMeta, adds AudioPluginState and migration/register infrastructure (IKnownAudioPluginsMigrationRegister, KnownAudioPluginsMigrationRegister), versioned known-plugins cache I/O, runtime-only attribute defaults, PluginType and VST attribute constants, refactors scanning/registration to use explicit states and placeholders, updates engine/VST/musesampler plumbing to the new types, and adapts/extends tests and mocks accordingly. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
framework/audioplugins/tests/knownaudiopluginsregistertest.cpp (1)
75-93: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winDrop the vestigial
RESOURCE_TYPE_TO_STRmap.
AudioResourceTypeis now a wire string, so this map is a single-entry identity for"VstPlugin"and silently rewrites any other type to"Undefined"via the fallback. New tests that introduce e.g.FluidSoundfontorMuseSamplerSoundPackwould produce wrong expected JSON without any signal. Set the field directly toinfo.meta.type.♻️ Proposed simplification
ByteArray pluginInfoListToJson(const std::vector<AudioPluginInfo>& infoList) const { - const std::map<AudioResourceType, std::string> RESOURCE_TYPE_TO_STR { - { "VstPlugin", "VstPlugin" }, - }; - JsonArray array; @@ JsonObject metaObj; metaObj.set("id", info.meta.id); - metaObj.set("type", muse::value(RESOURCE_TYPE_TO_STR, info.meta.type, "Undefined")); + metaObj.set("type", info.meta.type.toStdString());(adjust the conversion to match
AudioResourceType's underlying string type.)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/audioplugins/tests/knownaudiopluginsregistertest.cpp` around lines 75 - 93, Remove the vestigial RESOURCE_TYPE_TO_STR map and stop translating AudioResourceType via muse::value; instead set the JSON "type" directly from info.meta.type (adjusting conversion to the underlying string type as needed), i.e. replace the muse::value(RESOURCE_TYPE_TO_STR, info.meta.type, "Undefined") call in the metaObj.set("type", ...) expression with a direct use of info.meta.type (or info.meta.type.toStdString()/equivalent) so new resource types like FluidSoundfont or MuseSamplerSoundPack are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@framework/audio/engine/internal/audioengineconfiguration.cpp`:
- Line 44: The literal "FluidSoundfont" should be replaced with the canonical
FLUID_SOUNDFONT_TYPE_NAME constant to avoid duplicating the wire-string; update
the code in audioengineconfiguration.cpp to use the constant (e.g.
muse::audio::synth::FLUID_SOUNDFONT_TYPE_NAME or the correct namespace where
FLUID_SOUNDFONT_TYPE_NAME is declared) and ensure the existing include of
soundfonttypes.h provides that symbol or add the proper include/using directive
so the compiler can find FLUID_SOUNDFONT_TYPE_NAME.
In `@framework/audioplugins/audiopluginstypes.h`:
- Around line 81-85: AudioResourceMeta::operator< currently returns id <
other.id || vendor < other.vendor which is not a strict weak ordering; change
the comparator to perform a lexicographical comparison of all relevant members
(e.g., compare id first, if equal compare vendor, if equal compare type, then
attributes) so that the relation is transitive, antisymmetric, and returns false
when all compared fields are equal; implement this either with a sequence of
explicit comparisons or using std::tie(id, vendor, type, attributes) <
std::tie(...).
In `@framework/audioplugins/iknownaudiopluginsmigrationregister.h`:
- Line 32: CURRENT_KNOWN_AUDIO_PLUGINS_VERSION is set to 3 while the PR only
registers migrations 0->1 and 1->2, causing migrate() to fail for caches at
version 2; either implement and register the missing 2->3 migration (add the
migration handler and register it alongside the existing 0->1 and 1->2 entries)
or lower CURRENT_KNOWN_AUDIO_PLUGINS_VERSION to 2 to match the available
migrations so migrate() has a complete migration path. Ensure the change updates
the same symbols: CURRENT_KNOWN_AUDIO_PLUGINS_VERSION and the migration
registration logic used by migrate().
In `@framework/audioplugins/internal/knownaudiopluginsregister.cpp`:
- Around line 131-138: The object-branch currently treats a missing or
wrong-typed "version" or "plugins" field as defaults (fileVersion=0, empty
array) which can silently accept truncated/malformed envelopes; instead validate
the envelope: when json.isObject() fails to contain a numeric "version" and an
array-valued "plugins", log an error mentioning knownAudioPluginsPath and return
a Ret error (same style as the existing Unrecognized branch) rather than
defaulting; update the code around JsonObject root = json.rootObject(),
fileVersion = root.value("version").toInt(), and array =
root.value("plugins").toArray() to explicitly check root.contains("version") &&
root.value("version").isDouble()/isNumber and root.contains("plugins") &&
root.value("plugins").isArray(), extract/assign fileVersion and array only when
valid, otherwise return an error Ret.
- Around line 154-156: Runtime-only defaults are not overriding cached values
because emplace() retains existing entries in info.meta.attributes; change the
loop that iterates configuration()->runtimeAttributeDefaults() so it assigns
into info.meta.attributes (e.g., info.meta.attributes[kv.first] = kv.second)
instead of using emplace(), ensuring runtime defaults overwrite any legacy
cached entries.
In `@framework/audioplugins/internal/registeraudiopluginsscenario.cpp`:
- Around line 104-105: The calls to
knownPluginsRegister()->setPluginsState(result.missingPluginIds,
AudioPluginState::Missing) and
knownPluginsRegister()->setPluginsState(result.rediscoveredPluginIds,
AudioPluginState::Validated) currently discard their Ret values; update
RegisterAudioPluginScenario (or the surrounding function) to capture each
setPluginsState() return, check for failure, and abort/return an error (or
propagate the Ret) before proceeding to registration so the scan does not
continue on partial cache-write failures; ensure you reference the two calls
with result.missingPluginIds and result.rediscoveredPluginIds and
handle/log/propagate the Ret from setPluginsState() appropriately.
- Around line 57-63: The code currently maps each io::path_t to a single
CacheEntry (struct CacheEntry { AudioResourceId id; AudioPluginState state; })
which drops duplicate plugin IDs for the same binary path; change registered
from std::map<io::path_t, CacheEntry> to std::map<io::path_t,
std::vector<CacheEntry>> and, where you populate it from
knownPluginsRegister()->pluginInfoList(), push_back a CacheEntry for each info
(registered[info.path].push_back({ info.meta.id, info.state })). Update all
subsequent logic that formerly accessed a single CacheEntry by key (the blocks
handling Missing/Validated updates around the functions that iterate registered)
to iterate the vector and update each CacheEntry.state for matching
AudioResourceId values so every cached ID for a shared binary path is preserved
and updated.
In `@framework/audioplugins/iregisteraudiopluginsscenario.h`:
- Around line 49-53: The registerNewPlugins method currently returns void but
performs multiple fallible operations; change its signature from void
registerNewPlugins(const io::paths_t& pluginPaths, bool validate = true) to
return Ret and propagate that through implementations (short-circuit and return
the first Ret error encountered during cache write/load or any other failing
step). Update all classes implementing IRegisterAudioPluginsScenario to
implement Ret registerNewPlugins(...), ensure callers check and propagate the
Ret result instead of assuming success, and update the comment above the
declaration to reflect the new return value semantics.
In `@framework/vst/internal/vstmodulesrepository.cpp`:
- Around line 123-133: The code hard-codes u"Instrument" inside
VstModulesRepository::modulesMetaList to distinguish instruments from FX;
extract this literal into a named constant (e.g. INSTRUMENT_CATEGORY) declared
alongside CATEGORIES_ATTRIBUTE (suggest placing in vstpluginattrs.h or next to
PluginCategory) and replace the inline u"Instrument" usage with that constant so
both producer and consumer share the same symbol (update includes/usings as
needed to reference INSTRUMENT_CATEGORY where modulesMetaList reads
info.meta.attributeVal(muse::vst::CATEGORIES_ATTRIBUTE)).
In `@framework/vst/internal/vstpluginmetareader.cpp`:
- Around line 25-27: vstpluginmetareader.cpp relies on symbols
AUDIO_RESOURCE_TYPE_NAME and CATEGORIES_ATTRIBUTE but only pulls them
transitively via vsttypes.h; add an explicit include for "vstpluginattrs.h" at
the top of framework/vst/internal/vstpluginmetareader.cpp so the file directly
declares its dependency (locate the top includes near the existing `#include`
"vsttypes.h" and insert `#include` "vstpluginattrs.h" there).
In `@framework/vst/vstpluginattrs.h`:
- Around line 30-33: The header defines static const String CATEGORIES_ATTRIBUTE
which creates per-translation-unit copies; change its definition to use inline
const (matching AUDIO_RESOURCE_TYPE_NAME's inline constexpr style) so there is a
single shared definition of CATEGORIES_ATTRIBUTE across TUs; update the
declaration for the String named CATEGORIES_ATTRIBUTE to be inline const String
to avoid duplicate construction/destruction and address-inequality issues.
---
Outside diff comments:
In `@framework/audioplugins/tests/knownaudiopluginsregistertest.cpp`:
- Around line 75-93: Remove the vestigial RESOURCE_TYPE_TO_STR map and stop
translating AudioResourceType via muse::value; instead set the JSON "type"
directly from info.meta.type (adjusting conversion to the underlying string type
as needed), i.e. replace the muse::value(RESOURCE_TYPE_TO_STR, info.meta.type,
"Undefined") call in the metaObj.set("type", ...) expression with a direct use
of info.meta.type (or info.meta.type.toStdString()/equivalent) so new resource
types like FluidSoundfont or MuseSamplerSoundPack are preserved.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: e5095749-7e36-4068-a444-e9de5e141a23
📒 Files selected for processing (47)
framework/audio/common/audiotypes.hframework/audio/common/audioutils.hframework/audio/common/rpc/rpcpacker.hframework/audio/engine/internal/audioengineconfiguration.cppframework/audio/engine/internal/enginerpccontroller.cppframework/audio/engine/internal/synthesizers/fluidsynth/fluidresolver.cppframework/audio/tests/CMakeLists.txtframework/audio/tests/audioresourcetypes_tests.cppframework/audio/tests/rpcpacker_tests.cppframework/audioplugins/CMakeLists.txtframework/audioplugins/audiopluginsmodule.cppframework/audioplugins/audiopluginstypes.hframework/audioplugins/iaudiopluginmetareader.hframework/audioplugins/iaudiopluginsconfiguration.hframework/audioplugins/iknownaudiopluginsmigrationregister.hframework/audioplugins/iknownaudiopluginsregister.hframework/audioplugins/internal/audiopluginsconfiguration.cppframework/audioplugins/internal/audiopluginsconfiguration.hframework/audioplugins/internal/knownaudiopluginsmigrationregister.cppframework/audioplugins/internal/knownaudiopluginsmigrationregister.hframework/audioplugins/internal/knownaudiopluginsregister.cppframework/audioplugins/internal/knownaudiopluginsregister.hframework/audioplugins/internal/registeraudiopluginsscenario.cppframework/audioplugins/internal/registeraudiopluginsscenario.hframework/audioplugins/iregisteraudiopluginsscenario.hframework/audioplugins/tests/CMakeLists.txtframework/audioplugins/tests/audiopluginsutilstest.cppframework/audioplugins/tests/knownaudiopluginsmigrationregistertest.cppframework/audioplugins/tests/knownaudiopluginsregistertest.cppframework/audioplugins/tests/mocks/audiopluginmetareadermock.hframework/audioplugins/tests/mocks/audiopluginsconfigurationmock.hframework/audioplugins/tests/mocks/knownaudiopluginsmigrationregistermock.hframework/audioplugins/tests/mocks/knownaudiopluginsregistermock.hframework/audioplugins/tests/registeraudiopluginsscenariotest.cppframework/musesampler/internal/musesamplerresolver.cppframework/musesampler/musesamplertypes.hframework/vst/CMakeLists.txtframework/vst/internal/fx/vstfxprocessor.cppframework/vst/internal/synth/vstsynthesiser.cppframework/vst/internal/vstaudioclient.cppframework/vst/internal/vstaudioclient.hframework/vst/internal/vstmodulesrepository.cppframework/vst/internal/vstmodulesrepository.hframework/vst/internal/vstpluginmetareader.cppframework/vst/internal/vstpluginmetareader.hframework/vst/vstpluginattrs.hframework/vst/vsttypes.h
💤 Files with no reviewable changes (1)
- framework/audioplugins/tests/audiopluginsutilstest.cpp
|
|
||
| namespace muse::audioplugins { | ||
| inline AudioPluginType audioPluginTypeFromCategoriesString(const String& categoriesStr) | ||
| inline constexpr int CURRENT_KNOWN_AUDIO_PLUGINS_VERSION = 3; |
There was a problem hiding this comment.
The cache version is one step ahead of the registered migrations.
CURRENT_KNOWN_AUDIO_PLUGINS_VERSION is 3, but the concrete register in this PR only provides 0 -> 1 and 1 -> 2. Any legacy cache upgrade will still require a missing 2 -> 3 step and fail in migrate().
Minimal fix
-inline constexpr int CURRENT_KNOWN_AUDIO_PLUGINS_VERSION = 3;
+inline constexpr int CURRENT_KNOWN_AUDIO_PLUGINS_VERSION = 2;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| inline constexpr int CURRENT_KNOWN_AUDIO_PLUGINS_VERSION = 3; | |
| inline constexpr int CURRENT_KNOWN_AUDIO_PLUGINS_VERSION = 2; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@framework/audioplugins/iknownaudiopluginsmigrationregister.h` at line 32,
CURRENT_KNOWN_AUDIO_PLUGINS_VERSION is set to 3 while the PR only registers
migrations 0->1 and 1->2, causing migrate() to fail for caches at version 2;
either implement and register the missing 2->3 migration (add the migration
handler and register it alongside the existing 0->1 and 1->2 entries) or lower
CURRENT_KNOWN_AUDIO_PLUGINS_VERSION to 2 to match the available migrations so
migrate() has a complete migration path. Ensure the change updates the same
symbols: CURRENT_KNOWN_AUDIO_PLUGINS_VERSION and the migration registration
logic used by migrate().
|
/build |
| static const String PLAYBACK_SETUP_DATA_ATTRIBUTE(u"playbackSetupData"); | ||
| static const String CATEGORIES_ATTRIBUTE(u"categories"); | ||
|
|
||
| using AudioResourceId = muse::audioplugins::AudioResourceId; |
There was a problem hiding this comment.
Could you explain the purpose of this change? Not only does it create circular dependencies between the modules, but it also looks conceptually incorrect. AudioResource can be anything (soundfont, MuseSounds, etc.), not only audio plugins
…cycle
Decouple the audioplugins module from the audio module so it can host
generic plugin discovery for other apps (e.g. Audacity), and add a
versioned cache schema with app-registered migrations. This commit is
the framework-side half; it is paired with a main-repo commit that
wires the MuseScore-side migrations and bumps the muse/ submodule
pointer.
Decoupling:
- Plugin-shaped types (AudioResourceMeta, AudioResourceAttributes,
AudioResourceId, etc.) live in muse::audioplugins:: instead of
muse::audio::. Audio keeps `using` aliases for source compat.
- audioplugins::AudioResourceType becomes an opaque std::string. Apps
register their own plugin format identifiers; the audio module keeps
an engine-internal enum and converts at the boundary via
resourceTypeFromString() / resourceTypeName().
- Runtime-only attributes (skipped on save, re-injected on load) are
app-registered via IAudioPluginsConfiguration::setRuntimeAttributeDefaults
instead of hard-coded to audio::PLAYBACK_SETUP_DATA_ATTRIBUTE.
- HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE and CATEGORIES_ATTRIBUTE move out
of audioplugins (now framework-pure) into audio. meta.hasNativeEditorSupport()
is replaced by a free function audio::hasNativeEditorSupport(meta).
- AudioPluginType, IAudioPluginTypeDetector and AudioPluginInfo.type are
dropped from the framework. The Instrument/Fx classification was
runtime-only, never persisted, and MuseScore-shaped. The VST module
gains its own vst::PluginType and computes the category from meta on
demand.
Cache schema (version field added; bare-array legacy treated as v0):
- v0 -> v1: hasNativeEditorSupport moves from a top-level meta field
into meta.attributes ("true"/"false" strings).
- v1 -> v2: the boolean `enabled` flag becomes a `state` string. The
state lifecycle has four values:
* Discovered: scanner found the file but validation hasn't run yet
* Validated: validation succeeded; usable
* Missing: file no longer found at the previously known path
* Error: validation failed (errorCode carries detail)
- Both migrations are registered MuseScore-side via the new
IKnownAudioPluginsMigrationRegister.
Scanner behaviour:
- scanPlugins() now reports rediscoveredPluginIds (entries that were
Missing and have come back) alongside missingPluginIds.
- updatePluginsRegistry() uses the new
IKnownAudioPluginsRegister::setPluginsState() to mark removed
entries as Missing instead of erasing them, and rediscovered ones
back to Validated. Already-Missing entries stay Missing without
churn. unregisterPlugins() is kept for explicit UI-driven removal.
Typed attribute accessors:
- boolAttribute() / intAttribute() free helpers in muse::audioplugins
encode the on-disk "true"/"1" -> bool and digit-string -> int
conventions in one place, so callers don't reimplement them. Storage
stays as map<String, String>; no JSON / RPC / file-format change.
Tests cover migration chaining, legacy v0 array load, the v0->v1 and
v1->v2 transitions, and the Missing/Rediscovered scanner transitions.
Move CATEGORIES_ATTRIBUTE from audio/common/audiotypes.h into a new vst/vstpluginattrs.h header so VST consumers (including hosts that don't link the audio module) can use it without pulling audio. Fix VstPluginMetaReader::metaType() override return type to match the IAudioPluginMetaReader interface (audioplugins::AudioResourceType, not audio::AudioResourceType which is now an engine enum).
vstpluginmetareader's signature and body referenced muse::audio:: types and constants. AudioResourceMeta/AudioResourceMetaList actually live in audioplugins (audio just re-exports them); switch to use audioplugins:: directly. Move HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE alongside CATEGORIES_ATTRIBUTE in vst/vstpluginattrs.h so the file no longer needs muse::audio::. Audio's own makeReverbMeta keeps its constant in audiotypes.h (same string value).
Slims muse::audio::AudioResourceType to the formats the muse audio engine actually routes (FluidSoundfont, VstPlugin, NativeEffect, MuseSamplerSoundPack); the Audacity-only values (Lv2Plugin, AudioUnit, NyquistPlugin) move to Audacity's own EffectFamily enum, leaving the framework enum scoped to its own engine concerns. Each plugin-format module exports a wire-string constant (muse::vst::AUDIO_RESOURCE_TYPE_NAME, muse::musesampler::AUDIO_RESOURCE_TYPE_NAME, audio::FLUID_SOUNDFONT_TYPE_NAME, audio::NATIVE_EFFECT_TYPE_NAME) and producers use it instead of literal strings. New audio::isResourceType helper replaces scattered meta.type == "X" comparisons. A round-trip test (audioresourcetypes_tests) pins the wire strings to their canonical values so cache compatibility cannot drift.
Today's audacity launch tripped registerPlugins's m_loaded assertion because an old cache file with the obsolete 'enabled' field made load() return a migration error silently. Surface the failure where it actually originates: LOGE in KnownAudioPluginsRegister::load() at the JSON parse / unrecognized-root / migration-failure branches, with the cache path and the migration error text. Sharpen the messages produced by KnownAudioPluginsMigrationRegister::migrate() so the LOGE is actionable: future-version files tell the user the file is newer than the build expects, and missing migrators tell developers to register one in their AudioPluginsAppConfigModule. New tests pin the wording so a future regression that drops the actionable hint fails the build.
Wire the previously-reserved AudioPluginState::Discovered into the registration pipeline. registerNewPlugins writes a Discovered placeholder per path before spawning the validation subprocess; on subprocess return, removePluginsAtPath clears the placeholder before registerPlugin (Validated) or registerFailedPlugin (Error) writes a fresh entry. If the host crashes between placeholder-write and subprocess-return, the placeholder survives in the cache and scanPlugins() picks the path back up as 'new' on the next launch, so validation auto-resumes. removePluginsAtPath is the new register API and is mock-friendly. New scenario test covers the auto-resume path.
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).
processPluginsRegistration's per-iteration removePluginsAtPath ran in the main process and rewrote the JSON from the main's stale in-memory map, clobbering every Validated entry the subprocesses had accumulated — only the most recent survived. Move the placeholder removal into registerPlugin / registerFailedPlugin (subprocess side, fresh state); the main process now writes the register only once, via the existing load() at the end of registerNewPlugins. As a side benefit, registerPlugin becomes idempotent and a subprocess crash now leaves the Discovered placeholder intact (re-validated next launch) instead of pre-deleted.
registerNewPlugins() gains a `bool validate = true` parameter. When false, the paths are persisted as Discovered placeholders only and the out-of-process validation step is skipped. scanPlugins() already treats Discovered as re-validatable, so deferred-validation entries get re-offered on the next scan. Lets the app distinguish "user picked Skip on the validate prompt" from "user has never seen these paths" — Skip now records the plugins instead of discarding them.
Adds two tests covering the recent scan-flow changes, and updates the existing tests to reflect the new architecture (placeholder clearing moved from processPluginsRegistration into registerPlugin / registerFailedPlugin): - RegisterNewPlugins_ValidateFalsePersistsDiscoveredOnly: Skip records Discovered without spawning subprocesses. - RegisterNewPlugins_NoPerIterationClobber: main-process loop calls removePluginsAtPath once per path, never per iteration. - RegisterPlugin / RegisterFailedPlugin: assert subprocess-side clearing before registerPlugins. - UpdatePluginsRegistry_LeftoverDiscoveredRevalidates: expected removePluginsAtPath count drops 2->1.
The old `id < a.id || vendor < a.vendor` was not antisymmetric (could report a<b and b<a both true) and ignored type/attributes, so it mis-ordered AudioResourceMetaSet. Compare all four fields lexicographically.
A shell / multi-effect bundle hosts several plugin IDs at one path. The path->CacheEntry map kept only the last entry, so when such a binary disappeared or reappeared only one ID had its state transitioned. Map each path to a vector of entries and update them all.
Regression guard for the per-path multi-id tracking fix: a shell binary hosting two plugin ids must transition both to Missing when it disappears and both back to Validated when rediscovered, not just the last one.
load()'s object branch treated a missing/non-numeric "version" or a missing/non-array "plugins" as defaults (v0, empty array), so a truncated file loaded as "no plugins" and could be saved back that way. Validate the envelope and return an error instead.
load() used emplace() for runtime-only attribute defaults, which keeps a stale value if a legacy cache still carries that key. Assign instead so the current default always wins.
Both setPluginsState() calls write the cache and can fail; their Ret was discarded, so a partial state transition could be reported as success. Check each and return the error.
- vstpluginattrs.h: CATEGORIES_ATTRIBUTE static const -> inline const (one shared definition instead of a per-TU copy). - Extract the instrument-discriminator literal into vst::INSTRUMENT_CATEGORY and use it in vstmodulesrepository (shared producer/consumer symbol). - vstpluginmetareader.cpp: include vstpluginattrs.h directly (IWYU). - audioengineconfiguration.cpp: use FLUID_SOUNDFONT_TYPE_NAME instead of the raw "FluidSoundfont" wire-string literal.
CURRENT_KNOWN_AUDIO_PLUGINS_VERSION is 3 but the framework shipped only v0->v1 and v1->v2, so a framework-only consumer loading a v2 cache failed migrate(). Register a no-op v2->v3 default; apps that need real work (MuseScore's hasNativeEditorSupport lift) override the slot via registerMigration(2, ...). audacity's no-op v2->v3 is now redundant.
registerNewPlugins performed fallible cache writes/loads but returned void, so callers could not tell a partial scan from a clean one. Return Ret and short-circuit on the first failure; persistDiscoveredPlaceholders likewise propagates removePluginsAtPath / registerPlugins errors. updatePluginsRegistry now checks and propagates the result.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
framework/audioplugins/internal/knownaudiopluginsregister.cpp (2)
249-275:⚠️ Potential issue | 🟠 Major | ⚡ Quick winNotify observers after in-memory registry mutations.
setPluginsState()andremovePluginsAtPath()both mutatem_pluginInfoMap/m_pluginPathsand persist the result, but neither emitsm_pluginInfoListChanged. Subscribers only get notified onload(), so Missing/rediscovered/error transitions can stay invisible until a full reload.🔔 Minimal fix pattern
- return writePluginsInfo(); + Ret ret = writePluginsInfo(); + if (ret) { + m_pluginInfoListChanged.notify(); + } + return ret;Apply the same pattern to both mutators, or centralize it in a shared post-write helper so all registry mutations notify consistently.
Also applies to: 305-327
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/audioplugins/internal/knownaudiopluginsregister.cpp` around lines 249 - 275, setPluginsState and removePluginsAtPath currently mutate m_pluginInfoMap / m_pluginPaths and persist via writePluginsInfo but never notify observers, so update these mutators to emit m_pluginInfoListChanged after a successful in-memory change + successful write; specifically, in KnownAudioPluginsRegister::setPluginsState (and the analogous removePluginsAtPath implementation) call the existing notification/emitter for m_pluginInfoListChanged (or invoke a shared helper you create, e.g., notifyPluginInfoListChangedPostWrite) only when changed is true and writePluginsInfo() returns success, so subscribers see Missing/rediscovered/error transitions without requiring a full load.
156-171:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReject malformed plugin rows before inserting them.
load()now validates the root envelope, but it still inserts entries even whenmetaorpathis missing.metaFromJson({})yields emptyid/vendor/type, so one truncated row can create an empty-key record and poison later lookups.🛡️ Proposed fix
AudioPluginInfo info; info.meta = metaFromJson(object.value("meta").toObject()); for (const auto& kv : configuration()->runtimeAttributeDefaults()) { // Assign, don't emplace: runtime-only defaults must override any // stale value a legacy cache still carries for the same key. info.meta.attributes[kv.first] = kv.second; } info.path = object.value("path").toString(); info.state = audioPluginStateFromName(object.value("state").toStdString()); info.errorCode = object.value("errorCode").toInt(); + + if (!info.meta.isValid() || info.path.empty()) { + LOGE() << "Malformed known-audio-plugins entry at " << knownAudioPluginsPath; + return Ret(static_cast<int>(Ret::Code::UnknownError), + "Malformed known_audio_plugins.json entry"); + } m_pluginPaths.insert(info.path); m_pluginInfoMap.emplace(info.meta.id, std::move(info));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/audioplugins/internal/knownaudiopluginsregister.cpp` around lines 156 - 171, The loop currently inserts entries even when meta or path are missing, which allows empty keys (from metaFromJson({})) to poison lookups; update the loop that builds AudioPluginInfo (using metaFromJson, info.path, info.meta.id, m_pluginPaths, m_pluginInfoMap) to validate that info.meta.id and info.path are non-empty (and optionally that meta.vendor/type are present if required) and skip the entry (continue) when they are malformed so you do not call m_pluginPaths.insert(...) or m_pluginInfoMap.emplace(...) for invalid rows.framework/audioplugins/internal/knownaudiopluginsmigrationregister.cpp (1)
39-58: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winUse the canonical state-name helper in the v1→v2 migrator.
This migration hard-codes
"Validated"and"Error"even thoughaudioPluginStateName()is the canonical wire mapping. If those names ever drift, migrated caches and runtime parsing will diverge.♻️ Proposed fix
- obj.set("state", enabled ? std::string("Validated") : std::string("Error")); + obj.set("state", audioPluginStateName(enabled + ? AudioPluginState::Validated + : AudioPluginState::Error));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/audioplugins/internal/knownaudiopluginsmigrationregister.cpp` around lines 39 - 58, The migrator registered in registerMigration(1, ...) currently hardcodes "Validated" and "Error"; change it to use the canonical helper audioPluginStateName(...) so wire names stay consistent. Inside the lambda that iterates JsonArray plugins, replace the hardcoded strings with audioPluginStateName(AudioPluginState::Validated) for enabled==true and audioPluginStateName(AudioPluginState::Error) for enabled==false (keeping the same logic where "enabled" is removed and "state" set). Make sure the AudioPluginState symbol and audioPluginStateName function are available in this translation unit or include the proper header if missing.framework/vst/internal/vstpluginmetareader.cpp (1)
57-70:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftStop collapsing VST3 modules with multiple audio-effect classes into a single cache entry
VstPluginMetaReader::readMeta()emits only oneAudioResourceMetaperpluginPath(breaks after the firstkVstAudioEffectClass) and assigns a basename-onlymeta.idviaio::completeBasename(pluginPath).toStdString().VstPluginInstance::load()also breaks after the firstkVstAudioEffectClass, so it can’t select a specificClassInfoeven if more classes were discovered.- This prevents representing/transitioning multiple audio-effect classes from the same VST3 binary under the multi-ID-per-path behavior.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/vst/internal/vstpluginmetareader.cpp` around lines 57 - 70, VstPluginMetaReader::readMeta() currently stops after the first kVstAudioEffectClass and uses only the basename for meta.id (io::completeBasename(pluginPath).toStdString()), collapsing multiple audio-effect classes into one entry; remove the break and emit one AudioResourceMeta per ClassInfo in factory.classInfos() that matches kVstAudioEffectClass, setting meta.id to a unique multi-ID-per-path form (e.g. include pluginPath plus a class-specific identifier such as classInfo.id() or classInfo.name()) and preserve attributes/vendor as before; also update VstPluginInstance::load() to not break on the first class and to select/load a specific ClassInfo when a particular class ID is requested so the code can represent multiple audio-effect classes from the same VST3 binary.framework/audioplugins/internal/registeraudiopluginsscenario.cpp (1)
250-255:⚠️ Potential issue | 🟠 Major | ⚡ Quick winCheck
removePluginsAtPath()before writing replacement entries.Lines 254 and 293 discard the
Ret. If that cache update fails, the subprocess continues against stale state and can either trip the same-id guard or leave the stale placeholder on disk while returning the laterregisterPlugins()result.Proposed fix
- knownPluginsRegister()->removePluginsAtPath(pluginPath); + Ret ret = knownPluginsRegister()->removePluginsAtPath(pluginPath); + if (!ret) { + LOGE() << "Failed to clear existing plugin entry at path: " + << pluginPath.toStdString() << ", error: " << ret.toString(); + return ret; + } @@ - Ret ret = knownPluginsRegister()->registerPlugins(infoList); - return ret; + return knownPluginsRegister()->registerPlugins(infoList);Apply the same guard in
registerFailedPlugin()before building theErrorentry.Also applies to: 290-293
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/audioplugins/internal/registeraudiopluginsscenario.cpp` around lines 250 - 255, The call to knownPluginsRegister()->removePluginsAtPath(pluginPath) is returning a Ret that is currently ignored, which can leave stale placeholders or trip same-id guards; update the code around removePluginsAtPath (and the similar call around line ~290) to check the returned result and abort/return early (or propagate the error) if the cache update fails instead of continuing to write replacement entries; also apply the same pre-check in registerFailedPlugin() before constructing/writing the Error entry so you don't write error placeholders when removePluginsAtPath failed; reference removePluginsAtPath, knownPluginsRegister(), registerPlugins(), and registerFailedPlugin() when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@framework/audioplugins/internal/registeraudiopluginsscenario.cpp`:
- Around line 80-98: The loop currently only treats AudioPluginState::Discovered
as a reason to re-validate a path and only treats AudioPluginState::Missing as
rediscovered; update the logic to also consider AudioPluginState::Error as a
visible binary so paths with any CacheEntry.state == AudioPluginState::Error are
re-validated (add Error into the hasDiscovered predicate alongside Discovered)
and treat previously Error entries like Missing entries by pushing their
entry.id into result.rediscoveredPluginIds before erasing from registered so
errored plugins seen on disk get retried.
---
Outside diff comments:
In `@framework/audioplugins/internal/knownaudiopluginsmigrationregister.cpp`:
- Around line 39-58: The migrator registered in registerMigration(1, ...)
currently hardcodes "Validated" and "Error"; change it to use the canonical
helper audioPluginStateName(...) so wire names stay consistent. Inside the
lambda that iterates JsonArray plugins, replace the hardcoded strings with
audioPluginStateName(AudioPluginState::Validated) for enabled==true and
audioPluginStateName(AudioPluginState::Error) for enabled==false (keeping the
same logic where "enabled" is removed and "state" set). Make sure the
AudioPluginState symbol and audioPluginStateName function are available in this
translation unit or include the proper header if missing.
In `@framework/audioplugins/internal/knownaudiopluginsregister.cpp`:
- Around line 249-275: setPluginsState and removePluginsAtPath currently mutate
m_pluginInfoMap / m_pluginPaths and persist via writePluginsInfo but never
notify observers, so update these mutators to emit m_pluginInfoListChanged after
a successful in-memory change + successful write; specifically, in
KnownAudioPluginsRegister::setPluginsState (and the analogous
removePluginsAtPath implementation) call the existing notification/emitter for
m_pluginInfoListChanged (or invoke a shared helper you create, e.g.,
notifyPluginInfoListChangedPostWrite) only when changed is true and
writePluginsInfo() returns success, so subscribers see
Missing/rediscovered/error transitions without requiring a full load.
- Around line 156-171: The loop currently inserts entries even when meta or path
are missing, which allows empty keys (from metaFromJson({})) to poison lookups;
update the loop that builds AudioPluginInfo (using metaFromJson, info.path,
info.meta.id, m_pluginPaths, m_pluginInfoMap) to validate that info.meta.id and
info.path are non-empty (and optionally that meta.vendor/type are present if
required) and skip the entry (continue) when they are malformed so you do not
call m_pluginPaths.insert(...) or m_pluginInfoMap.emplace(...) for invalid rows.
In `@framework/audioplugins/internal/registeraudiopluginsscenario.cpp`:
- Around line 250-255: The call to
knownPluginsRegister()->removePluginsAtPath(pluginPath) is returning a Ret that
is currently ignored, which can leave stale placeholders or trip same-id guards;
update the code around removePluginsAtPath (and the similar call around line
~290) to check the returned result and abort/return early (or propagate the
error) if the cache update fails instead of continuing to write replacement
entries; also apply the same pre-check in registerFailedPlugin() before
constructing/writing the Error entry so you don't write error placeholders when
removePluginsAtPath failed; reference removePluginsAtPath,
knownPluginsRegister(), registerPlugins(), and registerFailedPlugin() when
making the change.
In `@framework/vst/internal/vstpluginmetareader.cpp`:
- Around line 57-70: VstPluginMetaReader::readMeta() currently stops after the
first kVstAudioEffectClass and uses only the basename for meta.id
(io::completeBasename(pluginPath).toStdString()), collapsing multiple
audio-effect classes into one entry; remove the break and emit one
AudioResourceMeta per ClassInfo in factory.classInfos() that matches
kVstAudioEffectClass, setting meta.id to a unique multi-ID-per-path form (e.g.
include pluginPath plus a class-specific identifier such as classInfo.id() or
classInfo.name()) and preserve attributes/vendor as before; also update
VstPluginInstance::load() to not break on the first class and to select/load a
specific ClassInfo when a particular class ID is requested so the code can
represent multiple audio-effect classes from the same VST3 binary.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0b7335ce-a62d-496e-8e72-ae56078837a2
📒 Files selected for processing (12)
framework/audio/engine/internal/audioengineconfiguration.cppframework/audioplugins/audiopluginstypes.hframework/audioplugins/internal/knownaudiopluginsmigrationregister.cppframework/audioplugins/internal/knownaudiopluginsregister.cppframework/audioplugins/internal/registeraudiopluginsscenario.cppframework/audioplugins/internal/registeraudiopluginsscenario.hframework/audioplugins/iregisteraudiopluginsscenario.hframework/audioplugins/tests/knownaudiopluginsmigrationregistertest.cppframework/audioplugins/tests/registeraudiopluginsscenariotest.cppframework/vst/internal/vstmodulesrepository.cppframework/vst/internal/vstpluginmetareader.cppframework/vst/vstpluginattrs.h
| // A Discovered placeholder means a prior run was interrupted | ||
| // before this path finished validating — re-validate the path. | ||
| const bool hasDiscovered = std::any_of(entries.cbegin(), entries.cend(), | ||
| [](const CacheEntry& e) { | ||
| return e.state == AudioPluginState::Discovered; | ||
| }); | ||
| if (hasDiscovered) { | ||
| result.newPluginPaths.push_back(path); | ||
| registered.erase(it); | ||
| continue; | ||
| } | ||
|
|
||
| // Every formerly Missing ID under this path is rediscovered. | ||
| for (const CacheEntry& entry : entries) { | ||
| if (entry.state == AudioPluginState::Missing) { | ||
| result.rediscoveredPluginIds.push_back(entry.id); | ||
| } | ||
| } | ||
| registered.erase(it); |
There was a problem hiding this comment.
Revalidate Error entries when the binary is seen again.
Lines 80-98 only retry Discovered paths and only transition Missing IDs. A plugin that previously landed in AudioPluginState::Error but is still present on disk gets erased from registered with no follow-up, so it stays permanently stuck in Error until the cache is deleted manually.
Proposed fix
- const bool hasDiscovered = std::any_of(entries.cbegin(), entries.cend(),
- [](const CacheEntry& e) {
- return e.state == AudioPluginState::Discovered;
- });
- if (hasDiscovered) {
+ const bool needsRevalidation = std::any_of(entries.cbegin(), entries.cend(),
+ [](const CacheEntry& e) {
+ return e.state == AudioPluginState::Discovered
+ || e.state == AudioPluginState::Error;
+ });
+ if (needsRevalidation) {
result.newPluginPaths.push_back(path);
registered.erase(it);
continue;
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@framework/audioplugins/internal/registeraudiopluginsscenario.cpp` around
lines 80 - 98, The loop currently only treats AudioPluginState::Discovered as a
reason to re-validate a path and only treats AudioPluginState::Missing as
rediscovered; update the logic to also consider AudioPluginState::Error as a
visible binary so paths with any CacheEntry.state == AudioPluginState::Error are
re-validated (add Error into the hasDiscovered predicate alongside Discovered)
and treat previously Error entries like Missing entries by pushing their
entry.id into result.rediscoveredPluginIds before erasing from registered so
errored plugins seen on disk get retried.
There was a problem hiding this comment.
sticky Error on automatic scans is intentional; retry is driven by user rescan (already supported)
There was a problem hiding this comment.
@luapmartin, understood — thanks for clarifying the design intent. Keeping Error sticky on automatic scans makes sense as a deliberate throttle to avoid repeatedly hammering a broken plugin on every launch, with the explicit user rescan as the intentional retry path.
✏️ Learnings added
Learnt from: luapmartin
Repo: musescore/muse_framework PR: 47
File: framework/audioplugins/internal/registeraudiopluginsscenario.cpp:80-98
Timestamp: 2026-05-22T15:12:37.428Z
Learning: In `framework/audioplugins/internal/registeraudiopluginsscenario.cpp` (muse_framework, C++), `AudioPluginState::Error` is intentionally sticky during automatic scans in `scanPlugins()`. Plugins in Error state are not re-validated automatically; retry is driven by an explicit user-initiated rescan. Do not flag this as a bug.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| using AudioResourceAttributes = muse::audioplugins::AudioResourceAttributes; | ||
| using AudioResourceMeta = muse::audioplugins::AudioResourceMeta; | ||
| using AudioResourceMetaList = muse::audioplugins::AudioResourceMetaList; | ||
| using AudioResourceMetaSet = muse::audioplugins::AudioResourceMetaSet; |
There was a problem hiding this comment.
I think we should simply break the connection between audio and audioplugins
i.e., consider them to have nothing in common.
Maybe it makes sense to rename audioplugins to something else, like userplugins
So, as it was:
-
Audio plugins loaded and created audio resources - the problem here is that Audacity don't use our audio, so they have "different" terms, resources, etc.
-
Now, as I understand it, our audio uses audio plugins, and their types - well, that's also not very clear, why should our audio depend on an auxiliary module...
-
Therefore, I think we need to break this connection completely:
-
MF audio data types exist in our terminology, whatever it may be.
-
There are data types for Audacity audio and effects.
-
There is a module that verifies and loads user plugins and its data types.
Each application has its own mapping:
PluginTypes (some meta) - AudioTypes (resource) for MF
PluginTypes (some meta) - EffectsTypes (...) for Audacity
Audio plugins must have their own types, and we need to convert one type to another
There was a problem hiding this comment.
Actually, I think right now it would be enough to do something like this:
- muse::audioplugins::AudioResourceId;
- muse::audio::AudioResourceId;
- Find a place to make
audioplugins::AudioResourceId plugId = ...
audio::AudioResourceId audioId = plugId;
or better
audio::AudioResourceId toAudioResourceId(const audioplugins::AudioResourceId& id)
{
return id;
}
i.e. make it one to one
This may look strange now, but in essence this is exactly what we need
Then we can continue to:
- Rename the types to reduce confusion and make them more meaningful
- Change types independently, we will only need to change the conversion code
load() validated the envelope but still inserted rows with a missing meta/path, creating empty-key records that poison the lookup map. Reject the file when an entry's id or path is empty.
…ugin The subprocess-side placeholder-clear discarded its Ret; a failed cache write would let stale state through. Capture, log and propagate it.
864f1c5 to
550d364
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@framework/audioplugins/audiopluginstypes.h`:
- Around line 143-162: The current conversion functions return an empty string
for AudioPluginState::Undefined which conflates undefined with unknown names;
update the mapping returned by detail::audioPluginStateNames() to include an
explicit entry for AudioPluginState::Undefined (e.g. "Undefined" or another
clear sentinel) and change audioPluginStateName(AudioPluginState) /
audioPluginStateFromName(const std::string&) to rely on that mapping so the name
lookup returns that explicit string and the reverse lookup maps that exact
string back to AudioPluginState::Undefined, making the conversion bijective and
unambiguous.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 823d4a18-a785-4fe8-887e-01b868c67c76
📒 Files selected for processing (34)
framework/audio/common/audiotypes.hframework/audio/common/audioutils.hframework/audio/common/rpc/rpcpacker.hframework/audio/engine/internal/audioengineconfiguration.cppframework/audio/engine/internal/enginerpccontroller.cppframework/audio/engine/internal/synthesizers/fluidsynth/fluidresolver.cppframework/audio/tests/CMakeLists.txtframework/audio/tests/audioresourcetypes_tests.cppframework/audio/tests/rpcpacker_tests.cppframework/audioplugins/CMakeLists.txtframework/audioplugins/audiopluginsmodule.cppframework/audioplugins/audiopluginstypes.hframework/audioplugins/iaudiopluginmetareader.hframework/audioplugins/iaudiopluginsconfiguration.hframework/audioplugins/iknownaudiopluginsmigrationregister.hframework/audioplugins/iknownaudiopluginsregister.hframework/audioplugins/internal/audiopluginsconfiguration.cppframework/audioplugins/internal/audiopluginsconfiguration.hframework/audioplugins/internal/knownaudiopluginsmigrationregister.cppframework/audioplugins/internal/knownaudiopluginsmigrationregister.hframework/audioplugins/internal/knownaudiopluginsregister.cppframework/audioplugins/internal/knownaudiopluginsregister.hframework/audioplugins/internal/registeraudiopluginsscenario.cppframework/audioplugins/internal/registeraudiopluginsscenario.hframework/audioplugins/iregisteraudiopluginsscenario.hframework/audioplugins/tests/CMakeLists.txtframework/audioplugins/tests/audiopluginsutilstest.cppframework/audioplugins/tests/knownaudiopluginsmigrationregistertest.cppframework/audioplugins/tests/knownaudiopluginsregistertest.cppframework/audioplugins/tests/mocks/audiopluginmetareadermock.hframework/audioplugins/tests/mocks/audiopluginsconfigurationmock.hframework/audioplugins/tests/mocks/knownaudiopluginsmigrationregistermock.hframework/audioplugins/tests/mocks/knownaudiopluginsregistermock.hframework/audioplugins/tests/registeraudiopluginsscenariotest.cpp
💤 Files with no reviewable changes (1)
- framework/audioplugins/tests/audiopluginsutilstest.cpp
audioPluginStateName(Undefined) returned "" — indistinguishable from an unknown name. Add an explicit Undefined<->"Undefined" entry so the mapping is bijective. writePluginsInfo still omits the state field for Undefined entries, so the JSON wire format is unchanged.
Audacity PR: audacity/audacity#10989
Musescore PR: musescore/MuseScore#33535
Revamp of the
audiopluginsframework module — decouple, version, state lifecycle.AudioResourceId,AudioResourceMeta,AudioResourceType, …) out ofmuse::audiointo the dedicatedmuse::audiopluginsmodule.muse::audiokeepsusingaliases, so existing callers compile unchanged.AudioPluginInfo'senabledboolean with anAudioPluginStatelifecycle:Discovered,Validated,Missing,Error.known_audio_plugins.jsoncache — a{version, plugins}envelope plus a migration register. The framework auto-registers v0→v1 (structural) and v1→v2 (enabled→state); apps register any later steps. Legacy bare-array files still load.AudioResourceTypeis now an opaque wire string; each plugin module owns its canonical identifier (vst::AUDIO_RESOURCE_TYPE_NAME = "VstPlugin", …). The engine-side enum was narrowed to the formats the engine actually routes.PluginTypeenum).Discoveredplaceholders are persisted so an interrupted scan auto-resumes next launch; stopping a scan no longer loses already-validated plugins;registerNewPlugins(paths, validate)can record plugins without validating them.Note:
IRegisterAudioPluginsScenario::unregisterRemovedPlugins()has no callers in either the framework or audacity — the scan flow now marks removed pluginsMissingviasetPluginsState()instead of hard-deleting. I think that depending on what are musescore needs we could consider dropping that method? or maybe keep it as a cleaner in case the known_audio_plugins.json get's "too big"... to be called from plugin manager.Build configuration
audacity: luapmartin/audacity/luapmartin/revamp-audio_plugins-module
audacity platforms: linux_x64 macos windows_x64
musescore: luapmartin/MuseScore/luapmartin/revamp-audio_plugins-module
musescore platforms: linux_x64 linux_arm64 macos windows_x64 windows_portable