Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
193 changes: 141 additions & 52 deletions framework/audio/common/audiotypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@

#pragma once

#include <map>
#include <variant>
#include <set>
#include <string>
#include <cmath>
#include <sstream>
#include <tuple>
#include <vector>

#include "global/types/number.h"
#include "global/types/secs.h"
Expand All @@ -39,6 +42,18 @@

#include "log.h"

// Audio module is now self-contained: it does NOT include
// audioplugins/audiopluginstypes.h. The bridge helpers below
// (toAudioResourceId, sourceTypeFromResourceType, etc.) take audioplugins
// id/type aliases - all std::string under the hood - so forward-declaring
// the aliases is enough. Pulling in the full plugin meta types is the
// caller's job at the bridge boundary; the engine never needs them.
namespace muse::audioplugins {
using PluginResourceId = std::string;
using PluginResourceIdList = std::vector<PluginResourceId>;
using PluginType = std::string;
}

namespace muse::audio {
using secs_t = muse::secs_t;
using msecs_t = muse::msecs_t;
Expand Down Expand Up @@ -168,86 +183,166 @@ struct AudioEngineConfig {
};

using AudioSourceName = std::string;
using AudioUnitConfig = std::map<std::string, std::string>;

// AudioResourceId addresses a resource within the audio pipeline;
// audioplugins::PluginResourceId is a plugin's identity in the cache.
// Both alias std::string today and convert one-to-one - the seam is
// documentary so the two roles do not drift.
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");
inline AudioResourceId toAudioResourceId(const muse::audioplugins::PluginResourceId& id)
{
return id;
}

enum class AudioResourceType {
Undefined = -1,
FluidSoundfont,
VstPlugin,
NativeEffect,
MuseSamplerSoundPack,
Lv2Plugin,
AudioUnit,
NyquistPlugin,
};
inline AudioResourceIdList toAudioResourceIdList(const muse::audioplugins::PluginResourceIdList& ids)
{
return AudioResourceIdList(ids.begin(), ids.end());
}

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" },
};
// Engine-domain resource meta is owned here by audio; cache-domain plugin
// meta lives in audioplugins::PluginMeta. The two structs share a layout
// today (id + type-as-string + vendor + attributes-as-string-map) but stay
// separate so neither module depends on the other at the type level. Apps
// bridge field-wise at the audio<->audioplugins boundary.
using AudioResourceVendor = std::string;
using AudioResourceAttributes = std::map<String, String>;

struct AudioResourceMeta {
AudioResourceId id;
AudioResourceVendor vendor;
AudioResourceAttributes attributes;
AudioResourceType type = AudioResourceType::Undefined;
bool hasNativeEditorSupport = false;
std::string type; // wire-string format identifier; see AudioResourceType enum below

const String& attributeVal(const String& key) const
{
auto search = attributes.find(key);
if (search != attributes.cend()) {
return search->second;
}

static String empty;
static const String empty;
return empty;
}

bool isValid() const
{
return !id.empty()
&& !vendor.empty()
&& type != AudioResourceType::Undefined;
return !id.empty() && !vendor.empty() && !type.empty();
}

bool operator==(const AudioResourceMeta& other) const
{
return id == other.id
&& vendor == other.vendor
&& type == other.type
&& hasNativeEditorSupport == other.hasNativeEditorSupport
&& attributes == other.attributes;
return id == other.id && vendor == other.vendor && type == other.type && attributes == other.attributes;
}

bool operator!=(const AudioResourceMeta& other) const
{
return !(*this == other);
}
bool operator!=(const AudioResourceMeta& other) const { return !(*this == other); }

bool operator<(const AudioResourceMeta& other) const
{
return id < other.id
|| vendor < other.vendor;
return std::tie(id, vendor, type, attributes)
< std::tie(other.id, other.vendor, other.type, other.attributes);
}
};

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

inline bool boolAttribute(const AudioResourceMeta& meta, const String& key, bool fallback = false)
{
const String& v = meta.attributeVal(key);
if (v.empty()) {
return fallback;
}
return v == u"true" || v == u"1";
}

inline int intAttribute(const AudioResourceMeta& meta, const String& key, int fallback = 0)
{
const String& v = meta.attributeVal(key);
if (v.empty()) {
return fallback;
}
bool ok = true;
int n = v.toInt(&ok);
return ok ? n : fallback;
}

// 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::PluginType (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,
};

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" },
};

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

inline AudioResourceType resourceTypeFromString(const std::string& name)
{
for (const auto& kv : RESOURCE_TYPE_NAMES) {
if (kv.second == name) {
return kv.first;
}
}
return AudioResourceType::Undefined;
}

inline bool isResourceType(const AudioResourceMeta& meta, AudioResourceType type)
{
return resourceTypeFromString(meta.type) == type;
}

static const String PLAYBACK_SETUP_DATA_ATTRIBUTE(u"playbackSetupData");
static const String HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE(u"hasNativeEditorSupport");

inline bool hasNativeEditorSupport(const AudioResourceMeta& meta)
{
return boolAttribute(meta, HAS_NATIVE_EDITOR_SUPPORT_ATTRIBUTE);
}

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" },
};

static const AudioResourceId MUSE_REVERB_ID("Muse Reverb");

enum class AudioFxType {
Expand Down Expand Up @@ -305,14 +400,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 @@ -384,16 +476,13 @@ enum class AudioSourceType {
MuseSampler
};

inline AudioSourceType sourceTypeFromResourceType(AudioResourceType type)
inline AudioSourceType sourceTypeFromResourceType(const muse::audioplugins::PluginType& 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::PluginType& 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 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 @@ -188,12 +188,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
Loading