diff --git a/ase/api.hh b/ase/api.hh index a9cc4f38..1389a47f 100644 --- a/ase/api.hh +++ b/ase/api.hh @@ -134,26 +134,27 @@ class Property : public virtual Emittable { protected: virtual ~Property () = 0; public: - virtual String ident () = 0; ///< Unique name (per owner) of this Property. - virtual String label () = 0; ///< Preferred user interface name. - virtual String nick () = 0; ///< Abbreviated user interface name, usually not more than 6 characters. - virtual String unit () = 0; ///< Units of the values within range. - virtual String hints () = 0; ///< Hints for parameter handling. - virtual String group () = 0; ///< Group name for parameters of similar function. - virtual String blurb () = 0; ///< Short description for user interface tooltips. - virtual String descr () = 0; ///< Elaborate description for help dialogs. - virtual double get_min () = 0; ///< Get the minimum property value, converted to double. - virtual double get_max () = 0; ///< Get the maximum property value, converted to double. - virtual double get_step () = 0; ///< Get the property value stepping, converted to double. - virtual void reset () = 0; ///< Assign default as normalized property value. - virtual Value get_value () = 0; ///< Get the native property value. - virtual bool set_value (const Value &v) = 0; ///< Set the native property value. - virtual double get_normalized () = 0; ///< Get the normalized property value, converted to double. - virtual bool set_normalized (double v) = 0; ///< Set the normalized property value as double. - virtual String get_text () = 0; ///< Get the current property value, converted to a text String. - virtual bool set_text (String v) = 0; ///< Set the current property value as a text String. - virtual bool is_numeric () = 0; ///< Whether the property settings can be represented as a floating point number. - virtual ChoiceS choices () = 0; ///< Enumerate choices for choosable properties. + virtual String ident () const = 0; ///< Unique name (per owner) of this Property. + virtual String label () const = 0; ///< Preferred user interface name. + virtual String nick () const = 0; ///< Abbreviated user interface name, usually not more than 6 characters. + virtual String unit () const = 0; ///< Units of the values within range. + virtual double get_min () const = 0; ///< Get the minimum property value, converted to double. + virtual double get_max () const = 0; ///< Get the maximum property value, converted to double. + virtual double get_step () const = 0; ///< Get the property value stepping, converted to double. + virtual void reset () = 0; ///< Assign default as normalized property value. + virtual Value get_value () const = 0; ///< Get the native property value. + virtual bool set_value (const Value &v) = 0; ///< Set the native property value. + virtual double get_normalized () const = 0; ///< Get the normalized property value, converted to double. + virtual bool set_normalized (double v) = 0; ///< Set the normalized property value as double. + virtual String get_text () const = 0; ///< Get the current property value, converted to a text String. + virtual bool set_text (String v) = 0; ///< Set the current property value as a text String. + virtual bool is_numeric () const = 0; ///< Whether the property settings can be represented as a floating point number. + virtual ChoiceS choices () const = 0; ///< Enumerate choices for choosable properties. + virtual StringS metadata () const = 0; ///< Get the list of additional metadata for a property. + String hints () const; ///< Hints for parameter handling (metadata). + String blurb () const; ///< Short description for user interface tooltips (metadata). + String descr () const; ///< Elaborate description, e.g. for help dialogs (metadata). + String group () const; ///< Group name for parameters of similar function (metadata). }; /// Base type for classes with Property interfaces. diff --git a/ase/clapdevice.cc b/ase/clapdevice.cc index 73eff318..75327fac 100644 --- a/ase/clapdevice.cc +++ b/ase/clapdevice.cc @@ -27,19 +27,15 @@ struct ClapPropertyImpl : public Property, public virtual EmittableImpl { String ident_, label_, module_; double min_value = NAN, max_value = NAN, default_value = NAN; public: - String ident () override { return ident_; } - String label () override { return label_; } - String nick () override { return parameter_guess_nick (label_); } - String unit () override { return ""; } - String hints () override { return ClapParamInfo::hints_from_param_info_flags (flags); } - String group () override { return module_; } - String blurb () override { return ""; } - String descr () override { return ""; } - double get_min () override { return min_value; } - double get_max () override { return max_value; } - double get_step () override { return is_stepped() ? 1 : 0; } - bool is_numeric () override { return true; } - bool is_stepped () { return strstr (hints().c_str(), ":stepped:"); } + String ident () const override { return ident_; } + String label () const override { return label_; } + String nick () const override { return parameter_guess_nick (label_); } + String unit () const override { return ""; } + double get_min () const override { return min_value; } + double get_max () const override { return max_value; } + double get_step () const override { return is_stepped() ? 1 : 0; } + bool is_numeric () const override { return true; } + bool is_stepped () const { return strstr (hints().c_str(), ":stepped:"); } void reset () override { set_value (default_value); } ClapPropertyImpl (ClapDeviceImplP device, const ClapParamInfo info) : device_ (device) @@ -54,7 +50,7 @@ struct ClapPropertyImpl : public Property, public virtual EmittableImpl { default_value = info.default_value; } ChoiceS - choices () override + choices () const override { const double mi = get_min(), ma = get_max(); const bool down = ma < mi; @@ -67,8 +63,17 @@ struct ClapPropertyImpl : public Property, public virtual EmittableImpl { } return choices; } + StringS + metadata () const override + { + StringS md; + md.push_back ("hints=" + ClapParamInfo::hints_from_param_info_flags (flags)); + if (!module_.empty()) + md.push_back ("group=" + module_); + return md; + } double - get_normalized () override + get_normalized () const override { const double mi = get_min(), ma = get_max(); const double value = get_value().as_double(); @@ -82,7 +87,7 @@ struct ClapPropertyImpl : public Property, public virtual EmittableImpl { return set_value (value); } String - get_text () override + get_text () const override { String txt; device_->handle_->param_get_value (param_id, &txt); @@ -94,7 +99,7 @@ struct ClapPropertyImpl : public Property, public virtual EmittableImpl { return device_->handle_->param_set_value (param_id, vstr); } Value - get_value () override + get_value () const override { return Value (device_->handle_->param_get_value (param_id)); } diff --git a/ase/engine.cc b/ase/engine.cc index 763c4c79..23b162a5 100644 --- a/ase/engine.cc +++ b/ase/engine.cc @@ -908,40 +908,40 @@ midi_driver_pref_list_choices (const CString &ident) static Preference pcm_driver_pref = Preference ({ "driver.pcm.devid", _("PCM Driver"), "", "auto", "ms", - { pcm_driver_pref_list_choices }, STANDARD, "", - _("Driver and device to be used for PCM input and output"), }, + { pcm_driver_pref_list_choices }, STANDARD, { + String ("descr=") + _("Driver and device to be used for PCM input and output"), } }, [] (const CString&,const Value&) { apply_driver_preferences(); }); static Preference synth_latency_pref = Preference ({ "driver.pcm.synth_latency", _("Synth Latency"), "", 15, "ms", - MinMaxStep { 0, 3000, 5 }, STANDARD + String ("step=5"), "", - _("Processing duration between input and output of a single sample, smaller values increase CPU load") }, + MinMaxStep { 0, 3000, 5 }, STANDARD + String ("step=5"), { + String ("descr=") + _("Processing duration between input and output of a single sample, smaller values increase CPU load"), } }, [] (const CString&,const Value&) { apply_driver_preferences(); }); static Preference midi1_driver_pref = Preference ({ "driver.midi1.devid", _("MIDI Controller (1)"), "", "auto", "ms", - { midi_driver_pref_list_choices }, STANDARD, "", - _("MIDI controller device to be used for MIDI input"), }, + { midi_driver_pref_list_choices }, STANDARD, { + String ("descr=") + _("MIDI controller device to be used for MIDI input"), } }, [] (const CString&,const Value&) { apply_driver_preferences(); }); static Preference midi2_driver_pref = Preference ({ "driver.midi2.devid", _("MIDI Controller (2)"), "", "auto", "ms", - { midi_driver_pref_list_choices }, STANDARD, "", - _("MIDI controller device to be used for MIDI input"), }, + { midi_driver_pref_list_choices }, STANDARD, { + String ("descr=") + _("MIDI controller device to be used for MIDI input"), } }, [] (const CString&,const Value&) { apply_driver_preferences(); }); static Preference midi3_driver_pref = Preference ({ "driver.midi3.devid", _("MIDI Controller (3)"), "", "auto", "ms", - { midi_driver_pref_list_choices }, STANDARD, "", - _("MIDI controller device to be used for MIDI input"), }, + { midi_driver_pref_list_choices }, STANDARD, { + String ("descr=") + _("MIDI controller device to be used for MIDI input"), } }, [] (const CString&,const Value&) { apply_driver_preferences(); }); static Preference midi4_driver_pref = Preference ({ "driver.midi4.devid", _("MIDI Controller (4)"), "", "auto", "ms", - { midi_driver_pref_list_choices }, STANDARD, "", - _("MIDI controller device to be used for MIDI input"), }, + { midi_driver_pref_list_choices }, STANDARD, { + String ("descr=") + _("MIDI controller device to be used for MIDI input"), } }, [] (const CString&,const Value&) { apply_driver_preferences(); }); static void diff --git a/ase/gadget.cc b/ase/gadget.cc index b3993f7b..a569e21d 100644 --- a/ase/gadget.cc +++ b/ase/gadget.cc @@ -207,10 +207,12 @@ PropertyBag GadgetImpl::property_bag () { auto add_prop = [this] (const Prop &prop, CString group) { - Param param = prop.param; - if (param.group.empty() && !group.empty()) - param.group = group; - this->props_.push_back (PropertyImpl::make_shared (param, prop.getter, prop.setter, prop.lister)); + if (!group.empty() && prop.param.fetch ("group").empty()) { + Param param = prop.param; + param.store ("group", group); + this->props_.push_back (PropertyImpl::make_shared (param, prop.getter, prop.setter, prop.lister)); + } else + this->props_.push_back (PropertyImpl::make_shared (prop.param, prop.getter, prop.setter, prop.lister)); // PropertyImpl &property = *gadget_.props_.back(); }; return PropertyBag (add_prop); diff --git a/ase/parameter.cc b/ase/parameter.cc index 46e65c33..628f11f8 100644 --- a/ase/parameter.cc +++ b/ase/parameter.cc @@ -72,31 +72,26 @@ Parameter::nick () const bool Parameter::has (const String &key) const { - return key == "ident" || kvpairs_search (details_, key) >= 0; + return key == "ident" || kvpairs_search (metadata_, key) >= 0; } String Parameter::fetch (const String &key) const { return_unless (key != "ident", cident); - const ssize_t i = kvpairs_search (details_, key); - return i >= 0 ? details_[i].data() + key.size() + 1 : ""; + return kvpairs_fetch (metadata_, key); } void Parameter::store (const String &key, const String &value) { assert_return (key.size() > 0); - if (key == "ident") { + if (key == "ident") cident = value; - return; + else { + const std::string kv = key + '=' + value; + kvpairs_assign (metadata_, kv); } - const ssize_t i = kvpairs_search (details_, key); - const std::string kv = key + '=' + value; - if (i >= 0) - details_[i] = kv; - else - details_.push_back (kv); } MinMaxStep @@ -319,7 +314,7 @@ Parameter::Parameter (const Param &initparam) { const Param &p = initparam; cident = !p.ident.empty() ? string_to_ncname (p.ident) : string_to_ncname (p.label, '_'); - details_ = p.details; + metadata_ = p.metadata; const auto choicesfuncp = std::get_if (&p.extras); MinMaxStep range; if (const auto rangep = std::get_if (&p.extras)) @@ -331,12 +326,6 @@ Parameter::Parameter (const Param &initparam) store ("nick", p.nick); if (!p.unit.empty()) store ("unit", p.unit); - if (!p.blurb.empty()) - store ("blurb", p.blurb); - if (!p.descr.empty()) - store ("descr", p.descr); - if (!p.group.empty()) - store ("group", p.group); const auto choicesp = std::get_if (&p.extras); bool isbool = false; if (choicesfuncp) @@ -427,10 +416,37 @@ ParameterMap::Entry::operator= (const Param ¶m) warning ("%s: duplicate %s: '%s' (param_id=%u)", FUNC, "label", param.label, id); } } - Param mparam = param; - if (mparam.group.empty()) - mparam.group = map.group; - pmap[id] = std::make_shared (mparam); + if (!map.group.empty() && param.fetch ("group").empty()) { + Param mparam = param; + mparam.store ("group", map.group); + pmap[id] = std::make_shared (mparam); + } else + pmap[id] = std::make_shared (param); +} + +// == Property == +String +Property::hints () const +{ + return kvpairs_fetch (metadata(), "hints"); +} + +String +Property::blurb () const +{ + return kvpairs_fetch (metadata(), "blurb"); +} + +String +Property::descr () const +{ + return kvpairs_fetch (metadata(), "descr"); +} + +String +Property::group () const +{ + return kvpairs_fetch (metadata(), "group"); } // == guess_nick == diff --git a/ase/parameter.hh b/ase/parameter.hh index 583ce937..26fbf65e 100644 --- a/ase/parameter.hh +++ b/ase/parameter.hh @@ -30,10 +30,9 @@ struct Param { String unit; ///< Units of the values within range. ExtraVals extras; ///< Min, max, stepping for double ranges or array of choices to select from. String hints; ///< Hints for parameter handling. - String blurb; ///< Short description for user interface tooltips. - String descr; ///< Elaborate description for help dialogs. - String group; ///< Group for parameters of similar function. - StringS details; ///< Array of "key=value" pairs. + StringS metadata; ///< Array of "key=value" pairs. + String fetch (const String &key) const { return kvpairs_fetch (metadata, key); } + void store (const String &key, const String &v) { kvpairs_assign (metadata, key + '=' + v); } static inline const String STORAGE = ":r:w:S:"; static inline const String STANDARD = ":r:w:S:G:"; }; @@ -55,6 +54,7 @@ struct Parameter { Value initial () const { return initial_; } bool has_hint (const String &hint) const; ChoiceS choices () const; + const StringS metadata () const { return metadata_; } MinMaxStep range () const; ///< Min, max, stepping for double ranges. bool is_numeric () const; bool is_choice () const { return has_hint ("choice"); } @@ -75,7 +75,7 @@ struct Parameter { static size_t match_choice (const ChoiceS &choices, const String &text); private: using ExtrasV = std::variant; - StringS details_; + StringS metadata_; ExtrasV extras_; Value initial_ = 0; }; @@ -100,27 +100,24 @@ class ParameterProperty : public EmittableImpl, public virtual Property { protected: ParameterC parameter_; public: - String ident () override { return parameter_->cident; } - String label () override { return parameter_->label(); } - String nick () override { return parameter_->nick(); } - String unit () override { return parameter_->unit(); } - String hints () override { return parameter_->hints(); } - String blurb () override { return parameter_->blurb(); } - String descr () override { return parameter_->descr(); } - String group () override { return parameter_->group(); } - double get_min () override { return std::get<0> (parameter_->range()); } - double get_max () override { return std::get<1> (parameter_->range()); } - double get_step () override { return std::get<2> (parameter_->range()); } - bool is_numeric () override { return parameter_->is_numeric(); } - ChoiceS choices () override { return parameter_->choices(); } - void reset () override { set_value (parameter_->initial()); } - double get_normalized () override { return !is_numeric() ? 0 : parameter_->normalize (get_double()); } + String ident () const override { return parameter_->cident; } + String label () const override { return parameter_->label(); } + String nick () const override { return parameter_->nick(); } + String unit () const override { return parameter_->unit(); } + double get_min () const override { return std::get<0> (parameter_->range()); } + double get_max () const override { return std::get<1> (parameter_->range()); } + double get_step () const override { return std::get<2> (parameter_->range()); } + bool is_numeric () const override { return parameter_->is_numeric(); } + ChoiceS choices () const override { return parameter_->choices(); } + StringS metadata () const override { return parameter_->metadata(); } + void reset () override { set_value (parameter_->initial()); } + double get_normalized () const override { return !is_numeric() ? 0 : parameter_->normalize (get_double()); } bool set_normalized (double v) override { return is_numeric() && set_value (parameter_->rescale (v)); } - String get_text () override { return parameter_->value_to_text (get_value()); } + String get_text () const override { return parameter_->value_to_text (get_value()); } bool set_text (String txt) override { set_value (parameter_->value_from_text (txt)); return !txt.empty(); } - Value get_value () override = 0; + Value get_value () const override = 0; bool set_value (const Value &v) override = 0; - double get_double () { return !is_numeric() ? 0 : get_value().as_double(); } + double get_double () const { return !is_numeric() ? 0 : get_value().as_double(); } ParameterC parameter () const { return parameter_; } Value initial () const { return parameter_->initial(); } MinMaxStep range () const { return parameter_->range(); } diff --git a/ase/processor.cc b/ase/processor.cc index 6f6f166d..48d3a9e9 100644 --- a/ase/processor.cc +++ b/ase/processor.cc @@ -938,17 +938,17 @@ class AudioPropertyImpl : public Property, public virtual EmittableImpl { double inflight_value_ = 0; uint64_t inflight_stamp_ = 0; public: - String ident () override { return parameter_->ident(); } - String label () override { return parameter_->label(); } - String nick () override { return parameter_->nick(); } - String unit () override { return parameter_->unit(); } - String hints () override { return parameter_->hints(); } - String group () override { return parameter_->group(); } - String blurb () override { return parameter_->blurb(); } - String descr () override { return parameter_->descr(); } - double get_min () override { const auto [fmin, fmax, step] = parameter_->range(); return fmin; } - double get_max () override { const auto [fmin, fmax, step] = parameter_->range(); return fmax; } - double get_step () override { const auto [fmin, fmax, step] = parameter_->range(); return step; } + String ident () const override { return parameter_->ident(); } + String label () const override { return parameter_->label(); } + String nick () const override { return parameter_->nick(); } + String unit () const override { return parameter_->unit(); } + String hints () const { return parameter_->hints(); } + String group () const { return parameter_->group(); } + String blurb () const { return parameter_->blurb(); } + String descr () const { return parameter_->descr(); } + double get_min () const override { const auto [fmin, fmax, step] = parameter_->range(); return fmin; } + double get_max () const override { const auto [fmin, fmax, step] = parameter_->range(); return fmax; } + double get_step () const override { const auto [fmin, fmax, step] = parameter_->range(); return step; } explicit AudioPropertyImpl (DeviceP devp, uint32_t id, ParameterC parameter) : device_ (devp), parameter_ (parameter), id_ (id) @@ -968,7 +968,7 @@ class AudioPropertyImpl : public Property, public virtual EmittableImpl { set_value (parameter_->initial()); } Value - get_value () override + get_value () const override { const AudioProcessorP proc = device_->_audio_processor(); const double value = inflight_stamp_ > proc->engine().frame_counter() ? inflight_value_ : AudioProcessor::param_peek_mt (proc, id_); @@ -999,7 +999,7 @@ class AudioPropertyImpl : public Property, public virtual EmittableImpl { return true; } double - get_normalized () override + get_normalized () const override { const AudioProcessorP proc = device_->_audio_processor(); const double value = inflight_stamp_ > proc->engine().frame_counter() ? inflight_value_ : AudioProcessor::param_peek_mt (proc, id_); @@ -1015,7 +1015,7 @@ class AudioPropertyImpl : public Property, public virtual EmittableImpl { return set_value (value); } String - get_text () override + get_text () const override { const AudioProcessorP proc = device_->_audio_processor(); const double value = inflight_stamp_ > proc->engine().frame_counter() ? inflight_value_ : AudioProcessor::param_peek_mt (proc, id_); @@ -1029,16 +1029,21 @@ class AudioPropertyImpl : public Property, public virtual EmittableImpl { return set_value (v); } bool - is_numeric () override + is_numeric () const override { // TODO: we have yet to implement non-numeric AudioProcessor parameters return true; } ChoiceS - choices () override + choices () const override { return parameter_->choices(); } + StringS + metadata () const override + { + return parameter_->metadata(); + } }; // == AudioProcessor::access_properties == diff --git a/ase/project.cc b/ase/project.cc index efd1ceaa..d775f2b1 100644 --- a/ase/project.cc +++ b/ase/project.cc @@ -808,11 +808,11 @@ ProjectImpl::create_properties () bag += Prop (getbpm, setbpm, { "bpm", _("Beats Per Minute"), _("BPM"), 90., "", MinMaxStep { 10., 1776., 0 }, STANDARD }); bag.group = _("Tuning"); bag += Prop (make_enum_getter (&musical_tuning_), make_enum_setter (&musical_tuning_), - { "musical_tuning", _("Musical Tuning"), _("Tuning"), uint32_t (MusicalTuning::OD_12_TET), "", {}, STANDARD, "", - _("The tuning system which specifies the tones or pitches to be used. " - "Due to the psychoacoustic properties of tones, various pitch combinations can " - "sound \"natural\" or \"pleasing\" when used in combination, the musical " - "tuning system defines the number and spacing of frequency values applied.") }, + { "musical_tuning", _("Musical Tuning"), _("Tuning"), uint32_t (MusicalTuning::OD_12_TET), "", {}, STANDARD, { + String ("descr=") + _("The tuning system which specifies the tones or pitches to be used. " + "Due to the psychoacoustic properties of tones, various pitch combinations can " + "sound \"natural\" or \"pleasing\" when used in combination, the musical " + "tuning system defines the number and spacing of frequency values applied."), } }, enum_lister); } diff --git a/ase/properties.cc b/ase/properties.cc index d608ab4f..11006688 100644 --- a/ase/properties.cc +++ b/ase/properties.cc @@ -135,7 +135,7 @@ Preference::~Preference() } Value -Preference::get_value () +Preference::get_value () const { PrefsMap &prefsmap = prefs_map(); PrefsValue &pv = prefsmap[parameter_->cident]; diff --git a/ase/properties.hh b/ase/properties.hh index c25791e4..064f889d 100644 --- a/ase/properties.hh +++ b/ase/properties.hh @@ -23,7 +23,7 @@ public: double getd () const { return const_cast (this)->get_value().as_double(); } bool set (const Value &value) { return set_value (value); } bool set (const String &string) { return set_value (string); } - Value get_value () override; + Value get_value () const override; bool set_value (const Value &v) override; static Value get (const String &ident); static PreferenceP find (const String &ident); @@ -44,7 +44,7 @@ using PropertyGetter = std::function; using PropertySetter = std::function; /// Function type to list Choice Property values. -using PropertyLister = std::function; +using PropertyLister = std::function; /// Structured initializer for PropertyImpl struct Prop { @@ -60,9 +60,9 @@ class PropertyImpl : public ParameterProperty { PropertyImpl (const Param&, const PropertyGetter&, const PropertySetter&, const PropertyLister&); public: ASE_DEFINE_MAKE_SHARED (PropertyImpl); - Value get_value () override { Value v; getter_ (v); return v; } + Value get_value () const override { Value v; getter_ (v); return v; } bool set_value (const Value &v) override { return setter_ (v); } - ChoiceS choices () override { return lister_ ? lister_ (*this) : parameter_->choices(); } + ChoiceS choices () const override { return lister_ ? lister_ (*this) : parameter_->choices(); } }; /// Helper to simplify property registrations. @@ -116,7 +116,7 @@ template concept IsEnum = std::is_enum_v; /// Helper to list Jsonipc::Enum<> type values as Choice. template requires IsEnum ChoiceS -enum_lister (ParameterProperty&) +enum_lister (const ParameterProperty&) { using EnumType = Jsonipc::Enum; ChoiceS choices; diff --git a/ase/strings.cc b/ase/strings.cc index 0da12a3d..5a7b46c7 100644 --- a/ase/strings.cc +++ b/ase/strings.cc @@ -1242,20 +1242,48 @@ kvpair_value (const String &key_value_pair) return eq ? key_value_pair.substr (eq - key_value_pair.c_str() + 1) : ""; } +String +kvpairs_fetch (const StringS &kvs, const String &key, bool casesensitive) +{ + const ssize_t i = kvpairs_search (kvs, key, casesensitive); + return i >= 0 ? kvs[i].data() + key.size() + 1 : ""; +} + ssize_t kvpairs_search (const StringS &kvs, const String &k, const bool casesensitive) { const size_t l = k.size(); for (size_t i = 0; i < kvs.size(); i++) if (kvs[i].size() > l && kvs[i][l] == '=') { - if (casesensitive && strncmp (kvs[i].data(), k.data(), l) == 0) - return i; - if (casesensitive && strncasecmp (kvs[i].data(), k.data(), l) == 0) - return i; + if (casesensitive) { + if (strncmp (kvs[i].data(), k.data(), l) == 0) + return i; + } else { // !casesensitive + if (strncasecmp (kvs[i].data(), k.data(), l) == 0) + return i; + } } return -1; } +bool +kvpairs_assign (StringS &kvs, const String &key_value_pair, bool casesensitive) +{ + const char *const eq = strchr (key_value_pair.c_str(), '='); + const String key = eq ? key_value_pair.substr (0, eq - key_value_pair.c_str()) : ""; + assert_return (key.size() > 0, false); + const ssize_t i = kvpairs_search (kvs, key, casesensitive); + if (key_value_pair.size() == key.size() + 1 && key_value_pair[key_value_pair.size()-1] == '=') { + // value is empty + if (i >= 0) + kvs.erase (kvs.begin() + i); // empty value, erase + } else if (i >= 0) + kvs[i] = key_value_pair; // replace + else + kvs.push_back (key_value_pair); // insert + return i >= 0; // replaced old value +} + // === String Options === static bool is_separator (char c) { return c == ';' || c == ':'; } diff --git a/ase/strings.hh b/ase/strings.hh index 2ab6c416..f43f8642 100644 --- a/ase/strings.hh +++ b/ase/strings.hh @@ -145,7 +145,9 @@ std::string_view string_option_find_value (const char *string, const char *featu // == Generic Key-Value-Pairs == String kvpair_key (const String &key_value_pair); String kvpair_value (const String &key_value_pair); -ssize_t kvpairs_search (const StringS &kvs, const String &k, bool casesensitive = true); +String kvpairs_fetch (const StringS &kvs, const String &key, bool casesensitive = true); +ssize_t kvpairs_search (const StringS &kvs, const String &key, bool casesensitive = true); +bool kvpairs_assign (StringS &kvs, const String &key_value_pair, bool casesensitive = true); // == Strings == /// Convenience Constructor for StringSeq or std::vector diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index 0d504bca..ef8374e5 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -443,7 +443,7 @@ class BlepSynth : public AudioProcessor { ChoiceS ve_model_cs; ve_model_cs += { "A", "Analog" }; ve_model_cs += { "F", "Flexible" }; - pmap[VE_MODEL] = Param { "ve_model", _("Envelope Model"), _("Model"), 0, "", std::move (ve_model_cs), "", "ADSR Model to be used" }; + pmap[VE_MODEL] = Param { "ve_model", _("Envelope Model"), _("Model"), 0, "", std::move (ve_model_cs), "", { String ("blurb=") + _("ADSR Model to be used"), } }; pmap[ATTACK] = Param { "attack", _("Attack"), _("A"), 20.0, "%", { 0, 100, }, }; pmap[DECAY] = Param { "decay", _("Decay"), _("D"), 30.0, "%", { 0, 100, }, }; @@ -464,14 +464,14 @@ class BlepSynth : public AudioProcessor { filter_type_choices += { "—"_uc, "Bypass Filter" }; filter_type_choices += { "LD"_uc, "Ladder Filter" }; filter_type_choices += { "SKF"_uc, "Sallen-Key Filter" }; - pmap[FILTER_TYPE] = Param { "filter_type", _("Filter Type"), _("Type"), FILTER_TYPE_LADDER, "", std::move (filter_type_choices), "", _("Filter Type to be used") }; + pmap[FILTER_TYPE] = Param { "filter_type", _("Filter Type"), _("Type"), FILTER_TYPE_LADDER, "", std::move (filter_type_choices), "", { String ("blurb=") + _("Filter Type to be used"), } }; ChoiceS ladder_mode_choices; ladder_mode_choices += { "LP1"_uc, "1 Pole Lowpass, 6dB/Octave" }; ladder_mode_choices += { "LP2"_uc, "2 Pole Lowpass, 12dB/Octave" }; ladder_mode_choices += { "LP3"_uc, "3 Pole Lowpass, 18dB/Octave" }; ladder_mode_choices += { "LP4"_uc, "4 Pole Lowpass, 24dB/Octave" }; - pmap[LADDER_MODE] = Param { "ladder_mode", _("Filter Mode"), _("Mode"), 1, "", std::move (ladder_mode_choices), "", "Ladder Filter Mode to be used" }; + pmap[LADDER_MODE] = Param { "ladder_mode", _("Filter Mode"), _("Mode"), 1, "", std::move (ladder_mode_choices), "", { String ("blurb=") + _("Ladder Filter Mode to be used"), } }; ChoiceS skfilter_mode_choices; skfilter_mode_choices += { "LP1"_uc, "1 Pole Lowpass, 6dB/Octave" }; @@ -490,7 +490,7 @@ class BlepSynth : public AudioProcessor { skfilter_mode_choices += { "HP4"_uc, "4 Pole Highpass, 24dB/Octave" }; skfilter_mode_choices += { "HP6"_uc, "6 Pole Highpass, 36dB/Octave" }; skfilter_mode_choices += { "HP8"_uc, "8 Pole Highpass, 48dB/Octave" }; - pmap[SKFILTER_MODE] = Param { "skfilter_mode", _("SKFilter Mode"), _("Mode"), 2, "", std::move (skfilter_mode_choices), "", "Sallen-Key Filter Mode to be used" }; + pmap[SKFILTER_MODE] = Param { "skfilter_mode", _("SKFilter Mode"), _("Mode"), 2, "", std::move (skfilter_mode_choices), "", { String ("blurb=") + _("Sallen-Key Filter Mode to be used"), } }; pmap.group = _("Filter Envelope"); pmap[FIL_ATTACK] = Param { "fil_attack", _("Attack"), _("A"), 40, "%", { 0, 100, }, }; diff --git a/devices/freeverb/freeverb.cc b/devices/freeverb/freeverb.cc index c38f3c0d..533d62dd 100644 --- a/devices/freeverb/freeverb.cc +++ b/devices/freeverb/freeverb.cc @@ -47,7 +47,8 @@ class Freeverb : public AudioProcessor { centries += { "Signflip 2000", _("Preserve May 2000 Freeverb damping sign flip") }; centries += { "VLC Damping", _("The VLC Freeverb version disables one damping feedback chain") }; centries += { "Normal Damping", _("Damping with sign correction as implemented in STK Freeverb") }; - pmap[MODE] = Param ("mode", _("Mode"), _("Mode"), 2, "", std::move (centries), "", _("Damping mode found in different Freeverb variants")); + pmap[MODE] = Param ("mode", _("Mode"), _("Mode"), 2, "", std::move (centries), "", + { String ("blurb=") + _("Damping mode found in different Freeverb variants"), }); pmap.group = _("Room Settings"); pmap[ROOMSIZE] = Param ("roomsize", _("Room size"), _("RS"), offsetroom + scaleroom * initialroom, _("size"), { offsetroom, offsetroom + scaleroom }); diff --git a/devices/liquidsfz/liquidsfz.cc b/devices/liquidsfz/liquidsfz.cc index 83e27197..4ba88d7f 100644 --- a/devices/liquidsfz/liquidsfz.cc +++ b/devices/liquidsfz/liquidsfz.cc @@ -126,7 +126,7 @@ class LiquidSFZ : public AudioProcessor { build_parameter_map() { ParameterMap pmap; - pmap[INSTRUMENT] = Param { "instrument", _("Instrument"), _("Inst"), "", "", {}, "", "Instrument File Name" }; + pmap[INSTRUMENT] = Param { "instrument", _("Instrument"), _("Inst"), "", "", {}, "", { String ("blurb=") + "Instrument File Name", } }; auto ccs = synth_.list_ccs(); for (const auto& cc_info : ccs) { diff --git a/devices/saturation/saturation.cc b/devices/saturation/saturation.cc index 1465a2ff..fdbdd8e2 100644 --- a/devices/saturation/saturation.cc +++ b/devices/saturation/saturation.cc @@ -37,7 +37,7 @@ class Saturation : public AudioProcessor { ChoiceS centries; centries += { "Soft/tanh", "Soft Saturation using the tanh function" }; centries += { "Hard", "Hard clipping" }; - pmap[MODE] = Param { "mode", "Mode", "Mode", 0, "", std::move (centries), "", "Saturation Function" }; + pmap[MODE] = Param { "mode", "Mode", "Mode", 0, "", std::move (centries), "", { String ("blurb=") + _("Saturation Function"), } }; pmap[MIX] = Param { "mix", "Mix dry/wet", "Mix", 100, "%", { 0, 100 } }; pmap[DRIVE] = Param { "drive", "Drive", "Drive", 0, "dB", { -6, 36 } };