Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
63109fe
[audioplugins] revamp framework module: decouple, version, state life…
luapmartin May 9, 2026
f1e01ae
[vst] decouple plugin attributes; fix metaType signature
luapmartin May 11, 2026
a5b4eba
[vst] make vstpluginmetareader fully audio-independent
luapmartin May 11, 2026
c5a54f2
[audio] strongly-typed plugin format identifiers
luapmartin May 11, 2026
5399fa9
[audioplugins] LOGE cache-load failures + sharper migration errors
luapmartin May 12, 2026
32a8f6b
[audioplugins] Discovered placeholders + auto-resume on next scan
luapmartin May 12, 2026
56ee120
[audioplugins] test register-level same-id-same-path guard
luapmartin May 12, 2026
6545c05
[audioplugins] framework auto-registers its own cache migrations
luapmartin May 13, 2026
49d9f80
[audioplugins] persist every validated plugin during an interrupted scan
luapmartin May 20, 2026
58f39b9
[audioplugins] allow registering new plugins without validation
luapmartin May 20, 2026
41fd89a
[audioplugins] tests for partial-scan persistence and validate=false
luapmartin May 20, 2026
ac51df2
[audioplugins] make AudioResourceMeta::operator< a strict weak ordering
luapmartin May 21, 2026
d4e0757
[audioplugins] scanPlugins: track every cached ID per binary path
luapmartin May 21, 2026
740f203
[audioplugins] test multi-id-per-path state transitions
luapmartin May 21, 2026
0df36f2
[audioplugins] reject malformed known_audio_plugins.json envelopes
luapmartin May 21, 2026
12e0d3d
[audioplugins] let runtime-attribute defaults override cached values
luapmartin May 21, 2026
8e2fc72
[audioplugins] updatePluginsRegistry: check setPluginsState results
luapmartin May 21, 2026
5c8c00a
[vst,audio] address CodeRabbit nitpicks (named constants, IWYU)
luapmartin May 21, 2026
785f073
[audioplugins] register a default no-op v2->v3 cache migration
luapmartin May 22, 2026
f306a44
[audioplugins] registerNewPlugins: return Ret instead of void
luapmartin May 22, 2026
c231431
[audioplugins] migrator v1->v2: use audioPluginStateName for wire str…
luapmartin May 22, 2026
61c6f54
[audioplugins] reject malformed rows when loading the plugin cache
luapmartin May 22, 2026
36ddee8
[audioplugins] check removePluginsAtPath result in register(Failed)Pl…
luapmartin May 22, 2026
cf7a324
[audioplugins] give AudioPluginState::Undefined an explicit wire name
luapmartin May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 69 additions & 68 deletions framework/audio/common/audiotypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@

#include "mpe/events.h"

#include "audioplugins/audiopluginstypes.h"

#include "log.h"

namespace muse::audio {
Expand Down Expand Up @@ -167,86 +169,91 @@ struct AudioEngineConfig {
};

using AudioSourceName = std::string;
using AudioResourceId = std::string;
using AudioResourceIdList = std::vector<AudioResourceId>;
using AudioResourceVendor = std::string;
using AudioResourceAttributes = std::map<String, String>;
using AudioUnitConfig = std::map<std::string, std::string>;

static const String PLAYBACK_SETUP_DATA_ATTRIBUTE(u"playbackSetupData");
static const String CATEGORIES_ATTRIBUTE(u"categories");

using AudioResourceId = muse::audioplugins::AudioResourceId;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

using AudioResourceIdList = muse::audioplugins::AudioResourceIdList;
using AudioResourceVendor = muse::audioplugins::AudioResourceVendor;
using AudioResourceAttributes = muse::audioplugins::AudioResourceAttributes;
using AudioResourceMeta = muse::audioplugins::AudioResourceMeta;
using AudioResourceMetaList = muse::audioplugins::AudioResourceMetaList;
using AudioResourceMetaSet = muse::audioplugins::AudioResourceMetaSet;
Copy link
Copy Markdown
Member

@igorkorsukov igorkorsukov May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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


// Wire-format identifiers for the formats the muse audio engine dispatches
// directly. The strings here are the canonical names persisted in the JSON
// plugin cache. VST and MuseSampler also own their own per-module copies of
// the matching string in their respective public headers; a round-trip test
// in muse_audio_tests asserts they stay in sync.
//
// constexpr std::string_view (not inline std::string) so that the values are
// available at compile time and don't participate in static initialization
// order — the RESOURCE_TYPE_NAMES map below depends on them.
inline constexpr std::string_view FLUID_SOUNDFONT_TYPE_NAME = "FluidSoundfont";
inline constexpr std::string_view NATIVE_EFFECT_TYPE_NAME = "NativeEffect";

// audio::AudioResourceType is an audio-engine-internal enum used to dispatch
// synth/fx routing. It is intentionally kept separate from the framework's
// opaque audioplugins::AudioResourceType (a std::string identifier persisted
// by the audioplugins module). Map between them at the boundary via
// resourceTypeFromString() / resourceTypeName(). The enum lists only the
// formats the audio engine actually routes; apps with broader plugin support
// (e.g. Audacity's LV2/AU/Nyquist) define their own enum and bridge.
enum class AudioResourceType {
Undefined = -1,
FluidSoundfont,
VstPlugin,
NativeEffect,
MuseSamplerSoundPack,
Lv2Plugin,
AudioUnit,
NyquistPlugin,
};

static const std::map<AudioResourceType, QString> RESOURCE_TYPE_MAP = {
{ AudioResourceType::Undefined, "undefined" },
{ AudioResourceType::MuseSamplerSoundPack, "muse_sampler_sound_pack" },
{ AudioResourceType::FluidSoundfont, "fluid_soundfont" },
{ AudioResourceType::VstPlugin, "vst_plugin" },
{ AudioResourceType::NativeEffect, "muse_plugin" },
{ AudioResourceType::Lv2Plugin, "lv2_plugin" },
{ AudioResourceType::AudioUnit, "audio_unit" },
{ AudioResourceType::NyquistPlugin, "nyquist_plugin" },
static const std::map<AudioResourceType, std::string> RESOURCE_TYPE_NAMES = {
{ AudioResourceType::FluidSoundfont, std::string(FLUID_SOUNDFONT_TYPE_NAME) },
{ AudioResourceType::VstPlugin, "VstPlugin" },
{ AudioResourceType::NativeEffect, std::string(NATIVE_EFFECT_TYPE_NAME) },
{ AudioResourceType::MuseSamplerSoundPack, "MuseSamplerSoundPack" },
};

struct AudioResourceMeta {
AudioResourceId id;
AudioResourceVendor vendor;
AudioResourceAttributes attributes;
AudioResourceType type = AudioResourceType::Undefined;
bool hasNativeEditorSupport = false;
inline const std::string& resourceTypeName(AudioResourceType type)
{
auto it = RESOURCE_TYPE_NAMES.find(type);
if (it != RESOURCE_TYPE_NAMES.end()) {
return it->second;
}
static const std::string empty;
return empty;
}

const String& attributeVal(const String& key) const
{
auto search = attributes.find(key);
if (search != attributes.cend()) {
return search->second;
inline AudioResourceType resourceTypeFromString(const std::string& name)
{
for (const auto& kv : RESOURCE_TYPE_NAMES) {
if (kv.second == name) {
return kv.first;
}

static String empty;
return empty;
}
return AudioResourceType::Undefined;
}

bool isValid() const
{
return !id.empty()
&& !vendor.empty()
&& type != AudioResourceType::Undefined;
}
inline bool isResourceType(const AudioResourceMeta& meta, AudioResourceType type)
{
return resourceTypeFromString(meta.type) == type;
}

bool operator==(const AudioResourceMeta& other) const
{
return id == other.id
&& vendor == other.vendor
&& type == other.type
&& hasNativeEditorSupport == other.hasNativeEditorSupport
&& attributes == other.attributes;
}
static const String PLAYBACK_SETUP_DATA_ATTRIBUTE(u"playbackSetupData");
static const String HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE(u"hasNativeEditorSupport");

bool operator!=(const AudioResourceMeta& other) const
{
return !(*this == other);
}
inline bool hasNativeEditorSupport(const AudioResourceMeta& meta)
{
return muse::audioplugins::boolAttribute(meta, HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE);
}

bool operator<(const AudioResourceMeta& other) const
{
return id < other.id
|| vendor < other.vendor;
}
static const std::map<AudioResourceType, QString> RESOURCE_TYPE_MAP = {
{ AudioResourceType::Undefined, "undefined" },
{ AudioResourceType::MuseSamplerSoundPack, "muse_sampler_sound_pack" },
{ AudioResourceType::FluidSoundfont, "fluid_soundfont" },
{ AudioResourceType::VstPlugin, "vst_plugin" },
{ AudioResourceType::NativeEffect, "muse_plugin" },
};

using AudioResourceMetaList = std::vector<AudioResourceMeta>;
using AudioResourceMetaSet = std::set<AudioResourceMeta>;

static const AudioResourceId MUSE_REVERB_ID("Muse Reverb");

enum class AudioFxType {
Expand Down Expand Up @@ -304,14 +311,11 @@ struct AudioFxParams {

AudioFxType type() const
{
switch (resourceMeta.type) {
switch (resourceTypeFromString(resourceMeta.type)) {
case AudioResourceType::VstPlugin: return AudioFxType::VstFx;
case AudioResourceType::NativeEffect: return AudioFxType::MuseFx;
case AudioResourceType::AudioUnit:
case AudioResourceType::Lv2Plugin:
case AudioResourceType::FluidSoundfont:
case AudioResourceType::MuseSamplerSoundPack:
case AudioResourceType::NyquistPlugin:
case AudioResourceType::Undefined: break;
}

Expand Down Expand Up @@ -383,16 +387,13 @@ enum class AudioSourceType {
MuseSampler
};

inline AudioSourceType sourceTypeFromResourceType(AudioResourceType type)
inline AudioSourceType sourceTypeFromResourceType(const muse::audioplugins::AudioResourceType& metaType)
{
switch (type) {
switch (resourceTypeFromString(metaType)) {
case AudioResourceType::FluidSoundfont: return AudioSourceType::Fluid;
case AudioResourceType::VstPlugin: return AudioSourceType::Vsti;
case AudioResourceType::MuseSamplerSoundPack: return AudioSourceType::MuseSampler;
case AudioResourceType::AudioUnit:
case AudioResourceType::Lv2Plugin:
case AudioResourceType::NativeEffect:
case AudioResourceType::NyquistPlugin:
case AudioResourceType::Undefined: break;
}

Expand Down
24 changes: 7 additions & 17 deletions framework/audio/common/audioutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ inline AudioResourceMeta makeReverbMeta()
{
AudioResourceMeta meta;
meta.id = MUSE_REVERB_ID;
meta.type = AudioResourceType::NativeEffect;
meta.type = NATIVE_EFFECT_TYPE_NAME;
meta.vendor = "Muse";
meta.hasNativeEditorSupport = true;
meta.attributes.emplace(HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE, u"true");

return meta;
}

inline String audioResourceTypeToString(const AudioResourceType& type)
inline String audioResourceTypeToString(const muse::audioplugins::AudioResourceType& metaType)
{
AudioResourceType type = resourceTypeFromString(metaType);
auto search = RESOURCE_TYPE_MAP.find(type);

if (search != RESOURCE_TYPE_MAP.end()) {
Expand All @@ -54,7 +55,7 @@ inline String audioSourceName(const AudioInputParams& params)
return params.resourceMeta.attributeVal(u"museName");
}

if (params.resourceMeta.type == audio::AudioResourceType::FluidSoundfont) {
if (isResourceType(params.resourceMeta, AudioResourceType::FluidSoundfont)) {
const String& presetName = params.resourceMeta.attributeVal(synth::PRESET_NAME_ATTRIBUTE);
if (!presetName.empty()) {
return presetName;
Expand Down Expand Up @@ -84,7 +85,7 @@ inline String audioSourceCategoryName(const AudioInputParams& params)
return params.resourceMeta.attributeVal(u"museCategory");
}

if (params.resourceMeta.type == audio::AudioResourceType::FluidSoundfont) {
if (isResourceType(params.resourceMeta, AudioResourceType::FluidSoundfont)) {
return params.resourceMeta.attributeVal(synth::SOUNDFONT_NAME_ATTRIBUTE);
}

Expand All @@ -109,17 +110,6 @@ inline AudioFxCategories audioFxCategoriesFromString(const String& str)

inline bool isOnlineAudioResource(const AudioResourceMeta& meta)
{
const String& attr = meta.attributeVal(u"isOnline");
if (attr.empty()) {
return false;
}

bool ok = true;
const int val = attr.toInt(&ok);
if (!ok) {
return false;
}

return val == 1;
return muse::audioplugins::boolAttribute(meta, u"isOnline");
}
}
4 changes: 2 additions & 2 deletions framework/audio/common/rpc/rpcpacker.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,12 @@ inline void unpack_custom(muse::msgpack::UnPacker& p, muse::audio::AudioFxCatego

inline void pack_custom(muse::msgpack::Packer& p, const muse::audio::AudioResourceMeta& value)
{
p.process(value.id, value.type, value.vendor, value.attributes, value.hasNativeEditorSupport);
p.process(value.id, value.type, value.vendor, value.attributes);
}

inline void unpack_custom(muse::msgpack::UnPacker& p, muse::audio::AudioResourceMeta& value)
{
p.process(value.id, value.type, value.vendor, value.attributes, value.hasNativeEditorSupport);
p.process(value.id, value.type, value.vendor, value.attributes);
}

inline void pack_custom(muse::msgpack::Packer& p, const muse::audio::AudioFxParams& value)
Expand Down
3 changes: 1 addition & 2 deletions framework/audio/engine/internal/audioengineconfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ static const AudioResourceMeta DEFAULT_AUDIO_RESOURCE_META = {
DEFAULT_SOUND_FONT_NAME,
"Fluid",
DEFAULT_AUDIO_RESOURCE_ATTRIBUTES,
AudioResourceType::FluidSoundfont,
false /*hasNativeEditor*/ };
std::string(FLUID_SOUNDFONT_TYPE_NAME) };

void AudioEngineConfiguration::setConfig(const AudioEngineConfig& conf)
{
Expand Down
4 changes: 1 addition & 3 deletions framework/audio/engine/internal/enginerpccontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,8 @@ void EngineRpcController::init()
}
};

AudioResourceType resourceType = params.source.resourceMeta.type;

// Not Fluid
if (resourceType != AudioResourceType::FluidSoundfont) {
if (!isResourceType(params.source.resourceMeta, AudioResourceType::FluidSoundfont)) {
addTrackAndSendResponse(msg, trackName, playbackData, params);
return make_response_delayed(msg);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,12 @@ void FluidResolver::refresh()

AudioResourceMeta chooseAutomaticMeta;
chooseAutomaticMeta.id = id;
chooseAutomaticMeta.type = AudioResourceType::FluidSoundfont;
chooseAutomaticMeta.type = FLUID_SOUNDFONT_TYPE_NAME;
chooseAutomaticMeta.vendor = FLUID_VENDOR_NAME;
chooseAutomaticMeta.attributes = {
{ PLAYBACK_SETUP_DATA_ATTRIBUTE, muse::mpe::GENERIC_SETUP_DATA_STRING },
{ SOUNDFONT_NAME_ATTRIBUTE, String::fromStdString(soundFont.name) }
};
chooseAutomaticMeta.hasNativeEditorSupport = false;

m_resourcesCache.emplace(id, SoundFontResource { soundFont.path, std::nullopt, std::move(chooseAutomaticMeta) });
}
Expand All @@ -129,7 +128,7 @@ void FluidResolver::refresh()

AudioResourceMeta meta;
meta.id = id;
meta.type = AudioResourceType::FluidSoundfont;
meta.type = FLUID_SOUNDFONT_TYPE_NAME;
meta.vendor = FLUID_VENDOR_NAME;
meta.attributes = {
{ PLAYBACK_SETUP_DATA_ATTRIBUTE, muse::mpe::GENERIC_SETUP_DATA_STRING },
Expand All @@ -138,7 +137,6 @@ void FluidResolver::refresh()
{ PRESET_BANK_ATTRIBUTE, String::number(preset.program.bank) },
{ PRESET_PROGRAM_ATTRIBUTE, String::number(preset.program.program) },
};
meta.hasNativeEditorSupport = false;

m_resourcesCache.emplace(id, SoundFontResource { soundFont.path, preset.program, std::move(meta) });
}
Expand Down
1 change: 1 addition & 0 deletions framework/audio/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ set(MODULE_TEST muse_audio_tests)
set(MODULE_TEST_SRC
${CMAKE_CURRENT_LIST_DIR}/rpcpacker_tests.cpp
${CMAKE_CURRENT_LIST_DIR}/alignbuffer_tests.cpp
${CMAKE_CURRENT_LIST_DIR}/audioresourcetypes_tests.cpp
)

include(SetupGTest)
Loading
Loading