Zrythm v2.0.0-alpha.1
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
parameter.h
1// SPDX-FileCopyrightText: © 2025-2026 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "dsp/audio_port.h"
7#include "dsp/cv_port.h"
8#include "dsp/graph_node.h"
9#include "dsp/midi_port.h"
10#include "utils/iobject_registry.h"
11#include "utils/math_utils.h"
12#include "utils/traits.h"
13#include "utils/typed_uuid_reference.h"
14#include "utils/units.h"
15#include "utils/uuid_identifiable_object.h"
16
17#include <QtQmlIntegration/qqmlintegration.h>
18
19#include <nlohmann/json_fwd.hpp>
20
21using namespace std::string_view_literals;
22
23namespace zrythm::dsp
24{
25
26class ParameterRange
27{
28 Q_GADGET
29 QML_VALUE_TYPE (parameterRange)
30
31public:
32 enum class Type : std::uint8_t
33 {
38
45
48
56
59
62
70 };
71 Q_ENUM (Type)
72
73
76 enum class Unit : std::uint8_t
77 {
78 None,
79 Hz,
80 MHz,
81 Db,
82 Degrees,
83 Seconds,
84
87
90 };
91
92 static utils::Utf8String unit_to_string (Unit unit);
93
94public:
95 ParameterRange () = default;
96
97 ParameterRange (Type type, float min, float max, float zero = 0.f, float def = 0.f)
98 : type_ (type), minf_ (min), maxf_ (max),
99 zerof_ (std::clamp (zero, min, max)), deff_ (std::clamp (def, min, max))
100 {
101 }
102
103 static ParameterRange make_toggle (bool default_val)
104 {
105 return { Type::Toggle, 0.f, 1.f, 0.f, default_val ? 1.f : 0.f };
106 }
107 static ParameterRange make_gain (float max_val)
108 {
109 return { Type::GainAmplitude, 0.f, max_val, 0.f, 1.f };
110 }
111
112 static ParameterRange make_enumeration (
113 std::vector<utils::Utf8String> labels,
114 size_t default_index = 0)
115 {
116 if (labels.empty ())
117 {
118 throw std::invalid_argument (
119 "Enumeration parameter requires at least one label");
120 }
121 if (default_index >= labels.size ())
122 {
123 throw std::invalid_argument (
124 "default_index out of range for enumeration labels");
125 }
126 const auto count = static_cast<float> (labels.size ());
127 ParameterRange range{
128 Type::Enumeration, 0.f, count - 1.f, 0.f,
129 static_cast<float> (default_index)
130 };
131 range.enum_labels_ = std::move (labels);
132 return range;
133 }
134
138 Q_INVOKABLE size_t enumCount () const { return enum_labels_.size (); }
139
143 Q_INVOKABLE size_t enumIndex (float normalized_val) const
144 {
145 const auto real = convertFrom0To1 (normalized_val);
146 assert (!enum_labels_.empty ());
147 const auto idx = static_cast<size_t> (std::max (std::round (real), 0.f));
148 return std::min (idx, enum_labels_.size () - 1);
149 }
150
154 Q_INVOKABLE float normalizedEnumValue (size_t index) const
155 {
156 assert (index < enum_labels_.size ());
157 if (enum_labels_.size () == 1)
158 return 0.f;
159 return convertTo0To1 (static_cast<float> (index));
160 }
161
165 const auto &enum_label (size_t index) const
166 {
167 return enum_labels_.at (index);
168 }
169
170 Q_INVOKABLE QString enumLabel (int index) const
171 {
172 if (index < 0 || static_cast<size_t> (index) >= enum_labels_.size ())
173 return {};
174 return enum_label (static_cast<size_t> (index)).to_qstring ();
175 }
176
177 template <utils::EnumType E> float normalized_from_enum (E value) const
178 {
179 assert (type_ == Type::Enumeration);
180 const auto index = static_cast<size_t> (value);
181 assert (index < enum_labels_.size ());
182 return normalizedEnumValue (index);
183 }
184
185 template <utils::EnumType E> E enum_value (float normalized_val) const
186 {
187 assert (type_ == Type::Enumeration);
188 return static_cast<E> (enumIndex (normalized_val));
189 }
190
191 constexpr float clamp_to_range (float val) const
192 {
193 return std::clamp (val, minf_, maxf_);
194 }
195
196 Q_INVOKABLE float convertFrom0To1 (float normalized_val) const;
197 Q_INVOKABLE float convertTo0To1 (float real_val) const;
198
199 Q_INVOKABLE bool isToggled (float normalized_val) const
200 {
201 assert (type_ == ParameterRange::Type::Toggle);
202 return utils::math::floats_equal (convertFrom0To1 (normalized_val), 1.f);
203 }
204
205 friend void to_json (nlohmann::json &j, const ParameterRange &p);
206 friend void from_json (const nlohmann::json &j, ParameterRange &p);
207
208public:
209 Type type_{ Type::Linear };
210
213
217 float minf_{ 0.f };
218 float maxf_{ 1.f };
219
225 float zerof_{ 0.f };
226
230 float deff_{ 0.f };
231
235 std::vector<utils::Utf8String> enum_labels_;
236
237 BOOST_DESCRIBE_CLASS (
238 ParameterRange,
239 (),
240 (type_, unit_, minf_, maxf_, zerof_, deff_),
241 (),
242 ())
243};
244
252class ProcessorParameter
253 : public utils::UuidIdentifiableObject<ProcessorParameter>,
255{
256 Q_OBJECT
257 Q_PROPERTY (
258 float baseValue READ baseValue WRITE setBaseValue NOTIFY baseValueChanged)
259 Q_PROPERTY (QString label READ label CONSTANT)
260 Q_PROPERTY (QString description READ description CONSTANT)
261 Q_PROPERTY (zrythm::dsp::ParameterRange range READ range CONSTANT)
262 Q_PROPERTY (bool automatable READ automatable CONSTANT)
263 QML_ELEMENT
264 QML_UNCREATABLE ("")
265
266public:
267 struct UniqueId final
268 : type_safe::strong_typedef<UniqueId, utils::Utf8String>,
269 type_safe::strong_typedef_op::equality_comparison<UniqueId>,
270 type_safe::strong_typedef_op::relational_comparison<UniqueId>
271 {
272 using type_safe::strong_typedef<UniqueId, utils::Utf8String>::strong_typedef;
273
274 explicit UniqueId () = default;
275
276 static_assert (utils::StrongTypedef<UniqueId>);
277
278 std::size_t hash () const { return qHash (type_safe::get (*this).view ()); }
279 };
280 static_assert (std::regular<UniqueId>);
281
282 using Resolver =
283 std::function<QPointer<ProcessorParameter> (const UniqueId &unique_id)>;
284
285 ProcessorParameter (
286 utils::IObjectRegistry &registry,
287 UniqueId unique_id,
288 ParameterRange range,
289 utils::Utf8String label,
290 QObject * parent = nullptr);
291
306 std::function<std::optional<float> (units::sample_t sample_position)>;
307
308 // ========================================================================
309 // QML Interface
310 // ========================================================================
311
312 QString label () const { return label_.to_qstring (); }
313 QString description () const { return description_->to_qstring (); }
314 bool automatable () const { return automatable_; }
315
316 const auto &range () const { return range_; }
317
318 float baseValue () const { return base_value_.load (); }
319 void setBaseValue (float newValue) [[clang::blocking]]
320 {
321 newValue = std::clamp (newValue, 0.f, 1.f);
322 if (qFuzzyCompare (base_value_, newValue))
323 return;
324 base_value_ = newValue;
325 Q_EMIT baseValueChanged (newValue);
326 }
327 Q_INVOKABLE void resetBaseValueToDefault ()
328 {
329 setBaseValue (range_.convertTo0To1 (range_.deff_));
330 }
331 Q_SIGNAL void baseValueChanged (float value);
332
337 Q_INVOKABLE float currentValue () const { return last_modulated_value_; }
338
344 */
345 Q_INVOKABLE float valueAfterAutomationApplied () const
346 {
347 return last_automated_value_.load ();
348 }
349
350 Q_INVOKABLE void beginUserGesture ()
351 {
352 during_gesture_.store (true);
353 Q_EMIT userGestureStarted ();
354 }
355 Q_INVOKABLE void endUserGesture ()
356 {
357 during_gesture_.store (false);
358 Q_EMIT userGestureFinished ();
359 }
360 Q_SIGNAL void userGestureStarted ();
361 Q_SIGNAL void userGestureFinished ();
362
363 // ========================================================================
364
365 // ========================================================================
366 // IProcessable Implementation
367 // ========================================================================
369 utils::Utf8String get_node_name () const override;
370
377 void process_block (
379 const dsp::ITransport &transport,
380 const dsp::TempoMap &tempo_map) noexcept override;
383 const graph::GraphNode * node,
384 units::sample_rate_t sample_rate,
385 units::sample_u32_t max_block_length) override;
386 void release_resources () override;
387
388 // ========================================================================
389
390 void set_automation_provider (AutomationValueProvider provider)
391 {
392 automation_value_provider_ = provider;
393 }
394 void unset_automation_provider () { automation_value_provider_.reset (); }
395
396 PortUuidReference get_modulation_input_port_ref () const
397 {
398 return modulation_input_uuid_;
399 }
400
401 void set_description (utils::Utf8String descr)
402 {
403 description_ = std::move (descr);
404 }
405
406 void set_automatable (bool automatable) { automatable_ = automatable; }
407
408 bool hidden () const { return hidden_; }
409
410 const auto &get_unique_id () const { return unique_id_; }
411
412private:
413 // Serialization keys
414 static constexpr auto kUniqueIdKey = "uniqueId"sv;
415 static constexpr auto kRangeKey = "range"sv;
416 static constexpr auto kLabelKey = "label"sv;
417 static constexpr auto kSymbolKey = "symbol"sv;
418 static constexpr auto kDescriptionKey = "description"sv;
419 static constexpr auto kAutomatableKey = "automatable"sv;
420 static constexpr auto kHiddenKey = "hidden"sv;
421 static constexpr auto kBaseValueKey = "baseValue"sv;
422 static constexpr auto kModulationSourcePortIdKey = "modulationSourcePortId"sv;
423 friend void to_json (nlohmann::json &j, const ProcessorParameter &p);
424 friend void from_json (const nlohmann::json &j, ProcessorParameter &p);
425
426private:
430 UniqueId unique_id_;
431
440 ParameterRange range_;
441
443 utils::Utf8String label_;
444
448 std::atomic<float> base_value_;
449
455 std::atomic_bool during_gesture_;
456
464 std::atomic<float> last_automated_value_;
465
475 std::atomic<float> last_modulated_value_;
476
483 PortUuidReference modulation_input_uuid_;
484
485 // Processing cache
486 dsp::CVPort * modulation_input_{};
487
493 std::optional<AutomationValueProvider> automation_value_provider_;
494
496 std::optional<utils::Utf8String> symbol_;
497
499 std::optional<utils::Utf8String> description_;
500
501 // Automatable (via automation provider or modulation)
502 bool automatable_{ true };
503
504 // Not on GUI
505 bool hidden_{};
506
507 BOOST_DESCRIBE_CLASS (
508 ProcessorParameter,
509 (utils::UuidIdentifiableObject<ProcessorParameter>),
510 (),
511 (),
512 (unique_id_,
513 range_,
514 label_,
515 base_value_,
516 modulation_input_uuid_,
517 symbol_,
518 description_,
519 automatable_,
520 hidden_))
521};
522
523inline auto
524format_as (const ProcessorParameter::UniqueId &id)
525{
526 return type_safe::get (id).view ();
527}
528
529using ProcessorParameterPtrVariant = std::variant<ProcessorParameter *>;
530}
531
532DEFINE_UUID_HASH_SPECIALIZATION (zrythm::dsp::ProcessorParameter::Uuid)
533
534namespace zrythm::dsp
535{
536
537using ProcessorParameterUuidReference =
538 utils::TypedUuidReference<ProcessorParameter>;
539
540} // namespace zrythm::dsp
541
542namespace std
543{
544template <> struct hash<zrythm::dsp::ProcessorParameter::UniqueId>
545{
546 size_t operator() (const zrythm::dsp::ProcessorParameter::UniqueId &id) const
547 {
548 return id.hash ();
549 }
550};
551}
Interface for transport.
Definition itransport.h:16
std::vector< utils::Utf8String > enum_labels_
Labels for Enumeration type parameters.
Definition parameter.h:235
float minf_
Minimum, maximum and zero values for this parameter.
Definition parameter.h:217
Q_INVOKABLE size_t enumCount() const
Returns the number of enum entries.
Definition parameter.h:138
const auto & enum_label(size_t index) const
Returns the label for a given enum index.
Definition parameter.h:165
Q_INVOKABLE float normalizedEnumValue(size_t index) const
Returns the normalized value for a given enum index.
Definition parameter.h:154
float zerof_
The zero position of the port.
Definition parameter.h:225
Unit unit_
Parameter unit.
Definition parameter.h:212
Q_INVOKABLE size_t enumIndex(float normalized_val) const
Converts a normalized value to an enum index.
Definition parameter.h:143
@ GainAmplitude
Parameter is a gain amplitude (0-2.0).
Definition parameter.h:55
@ Enumeration
Port's only reasonable values are its scale points.
Definition parameter.h:61
@ Logarithmic
Logarithmic-scaled float parameter.
Definition parameter.h:58
@ Linear
Linearly-scaled float parameter.
Definition parameter.h:37
@ Integer
Whether the port is an integer.
Definition parameter.h:47
@ Toggle
Whether the port is a toggle (on/off).
Definition parameter.h:44
@ Trigger
Trigger parameters are set to on to trigger a change during processing and then turned off at the end...
Definition parameter.h:69
Unit
Unit to be displayed in the UI.
Definition parameter.h:77
float deff_
Default value.
Definition parameter.h:230
std::function< std::optional< float >(units::sample_t sample_position)> AutomationValueProvider
Provides the automation value for a given sample position.
Definition parameter.h:304
void prepare_for_processing(const graph::GraphNode *node, units::sample_rate_t sample_rate, units::sample_u32_t max_block_length) override
Called to allocate resources required for processing.
void release_resources() override
Called to release resources allocated by prepare_for_processing().
void process_block(dsp::graph::ProcessBlockInfo time_nfo, const dsp::ITransport &transport, const dsp::TempoMap &tempo_map) noexcept override
Processes automation and modulation.
Q_INVOKABLE float currentValue() const
Returns the current (normalized) value after any automation and modulation has been applied.
Definition parameter.h:336
utils::Utf8String get_node_name() const override
Returns a human friendly name of the node.
Q_INVOKABLE float valueAfterAutomationApplied() const
Returns the value after automation, but before modulation has been applied.
Definition parameter.h:344
Represents a node in a DSP graph.
Definition graph_node.h:173
Interface for objects that can be processed in the DSP graph.
Definition graph_node.h:99
Abstract interface for a UUID-keyed object registry.
Lightweight UTF-8 string wrapper with safe conversions.
Definition utf8_string.h:37
CRTP base that adds a typed UUID strong-typedef to a class hierarchy.
constexpr bool floats_equal(T a, T b)
Checks if 2 floating point numbers are equal.
Definition math_utils.h:74
String utilities.
Common struct to pass around during processing to avoid repeating the data in function arguments.
Definition graph_node.h:51