Zrythm v2.0.0-DEV
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/math_utils.h"
11#include "utils/units.h"
12#include "utils/uuid_identifiable_object.h"
13
14#include <QtQmlIntegration/qqmlintegration.h>
15
16#include <nlohmann/json_fwd.hpp>
17
18namespace zrythm::dsp
19{
20
21class ParameterRange
22{
23 Q_GADGET
24 QML_VALUE_TYPE (parameterRange)
25
26public:
27 enum class Type : std::uint8_t
28 {
33
40
43
51
54
57
65 };
66 Q_ENUM (Type)
67
68
71 enum class Unit : std::uint8_t
72 {
73 None,
74 Hz,
75 MHz,
76 Db,
77 Degrees,
78 Seconds,
79
82
85 };
86
87 static utils::Utf8String unit_to_string (Unit unit);
88
89public:
90 ParameterRange () = default;
91
92 ParameterRange (Type type, float min, float max, float zero = 0.f, float def = 0.f)
93 : type_ (type), minf_ (min), maxf_ (max),
94 zerof_ (std::clamp (zero, min, max)), deff_ (std::clamp (def, min, max))
95 {
96 }
97
98 static ParameterRange make_toggle (bool default_val)
99 {
100 return { Type::Toggle, 0.f, 1.f, 0.f, default_val ? 1.f : 0.f };
101 }
102 static ParameterRange make_gain (float max_val)
103 {
104 return { Type::GainAmplitude, 0.f, max_val, 0.f, 1.f };
105 }
106
107 constexpr float clamp_to_range (float val) const
108 {
109 return std::clamp (val, minf_, maxf_);
110 }
111
112 Q_INVOKABLE float convertFrom0To1 (float normalized_val) const;
113 Q_INVOKABLE float convertTo0To1 (float real_val) const;
114
115 bool is_toggled (float normalized_val) const
116 {
117 assert (type_ == ParameterRange::Type::Toggle);
118 return utils::math::floats_equal (convertFrom0To1 (normalized_val), 1.f);
119 }
120
121 friend void to_json (nlohmann::json &j, const ParameterRange &p);
122 friend void from_json (const nlohmann::json &j, ParameterRange &p);
123
124public:
125 Type type_{ Type::Linear };
126
129
133 float minf_{ 0.f };
134 float maxf_{ 1.f };
135
141 float zerof_{ 0.f };
142
146 float deff_{ 0.f };
147
148 BOOST_DESCRIBE_CLASS (
150 (),
151 (type_, unit_, minf_, maxf_, zerof_, deff_),
152 (),
153 ())
154};
155
163class ProcessorParameter
164 : public QObject,
166 public utils::UuidIdentifiableObject<ProcessorParameter>
167{
168 Q_OBJECT
169 Q_PROPERTY (
170 float baseValue READ baseValue WRITE setBaseValue NOTIFY baseValueChanged)
171 Q_PROPERTY (QString label READ label CONSTANT)
172 Q_PROPERTY (QString description READ description CONSTANT)
173 Q_PROPERTY (zrythm::dsp::ParameterRange range READ range CONSTANT)
174 Q_PROPERTY (bool automatable READ automatable CONSTANT)
175 QML_ELEMENT
176 QML_UNCREATABLE ("")
177
178public:
179 struct UniqueId final
180 : type_safe::strong_typedef<UniqueId, utils::Utf8String>,
181 type_safe::strong_typedef_op::equality_comparison<UniqueId>,
182 type_safe::strong_typedef_op::relational_comparison<UniqueId>
183 {
184 using type_safe::strong_typedef<UniqueId, utils::Utf8String>::strong_typedef;
185
186 explicit UniqueId () = default;
187
188 static_assert (StrongTypedef<UniqueId>);
189
190 std::size_t hash () const { return qHash (type_safe::get (*this).view ()); }
191 };
192 static_assert (std::regular<UniqueId>);
193
194 using Resolver =
195 std::function<QPointer<ProcessorParameter> (const UniqueId &unique_id)>;
196
197 ProcessorParameter (
198 PortRegistry &port_registry,
199 UniqueId unique_id,
200 ParameterRange range,
201 utils::Utf8String label,
202 QObject * parent = nullptr);
203
218 std::function<std::optional<float> (units::sample_t sample_position)>;
219
220 // ========================================================================
221 // QML Interface
222 // ========================================================================
223
224 QString label () const { return label_.to_qstring (); }
225 QString description () const { return description_->to_qstring (); }
226 bool automatable () const { return automatable_; }
227
228 ParameterRange range () const { return range_; }
229
230 float baseValue () const { return base_value_.load (); }
231 void setBaseValue (float newValue) [[clang::blocking]]
232 {
233 newValue = std::clamp (newValue, 0.f, 1.f);
234 if (qFuzzyCompare (base_value_, newValue))
235 return;
236 base_value_ = newValue;
237 Q_EMIT baseValueChanged (newValue);
238 }
239 Q_INVOKABLE void resetBaseValueToDefault ()
240 {
241 setBaseValue (range_.convertTo0To1 (range_.deff_));
242 }
243 Q_SIGNAL void baseValueChanged (float value);
244
249 Q_INVOKABLE float currentValue () const { return last_modulated_value_; }
250
256 */
257 Q_INVOKABLE float valueAfterAutomationApplied () const
258 {
259 return last_automated_value_.load ();
260 }
261
262 Q_INVOKABLE void beginUserGesture ()
263 {
264 during_gesture_.store (true);
265 Q_EMIT userGestureStarted ();
266 }
267 Q_INVOKABLE void endUserGesture ()
268 {
269 during_gesture_.store (false);
270 Q_EMIT userGestureFinished ();
271 }
272 Q_SIGNAL void userGestureStarted ();
273 Q_SIGNAL void userGestureFinished ();
274
275 // ========================================================================
276
277 // ========================================================================
278 // IProcessable Implementation
279 // ========================================================================
281 utils::Utf8String get_node_name () const override;
282
289 void process_block (
291 const dsp::ITransport &transport,
292 const dsp::TempoMap &tempo_map) noexcept override;
295 const graph::GraphNode * node,
296 units::sample_rate_t sample_rate,
297 units::sample_u32_t max_block_length) override;
298 void release_resources () override;
299
300 // ========================================================================
301
302 void set_automation_provider (AutomationValueProvider provider)
303 {
304 automation_value_provider_ = provider;
305 }
306 void unset_automation_provider () { automation_value_provider_.reset (); }
307
308 PortUuidReference get_modulation_input_port_ref () const
309 {
310 return modulation_input_uuid_;
311 }
312
313 void set_description (utils::Utf8String descr)
314 {
315 description_ = std::move (descr);
316 }
317
318 void set_automatable (bool automatable) { automatable_ = automatable; }
319
320 bool hidden () const { return hidden_; }
321
322 const auto &get_unique_id () const { return unique_id_; }
323
324private:
325 // Serialization keys
326 static constexpr auto kUniqueIdKey = "uniqueId"sv;
327 static constexpr auto kRangeKey = "range"sv;
328 static constexpr auto kLabelKey = "label"sv;
329 static constexpr auto kSymbolKey = "symbol"sv;
330 static constexpr auto kDescriptionKey = "description"sv;
331 static constexpr auto kAutomatableKey = "automatable"sv;
332 static constexpr auto kHiddenKey = "hidden"sv;
333 static constexpr auto kBaseValueKey = "baseValue"sv;
334 static constexpr auto kModulationSourcePortIdKey = "modulationSourcePortId"sv;
335 friend void to_json (nlohmann::json &j, const ProcessorParameter &p);
336 friend void from_json (const nlohmann::json &j, ProcessorParameter &p);
337
338private:
342 UniqueId unique_id_;
343
344 ParameterRange range_;
345
347 utils::Utf8String label_;
348
352 std::atomic<float> base_value_;
353
359 std::atomic_bool during_gesture_;
360
368 std::atomic<float> last_automated_value_;
369
379 std::atomic<float> last_modulated_value_;
380
387 PortUuidReference modulation_input_uuid_;
388
389 // Processing cache
390 dsp::CVPort * modulation_input_{};
391
397 std::optional<AutomationValueProvider> automation_value_provider_;
398
400 std::optional<utils::Utf8String> symbol_;
401
403 std::optional<utils::Utf8String> description_;
404
405 // Automatable (via automation provider or modulation)
406 bool automatable_{ true };
407
408 // Not on GUI
409 bool hidden_{};
410
411 BOOST_DESCRIBE_CLASS (
412 ProcessorParameter,
413 (utils::UuidIdentifiableObject<ProcessorParameter>),
414 (),
415 (),
416 (unique_id_,
417 range_,
418 label_,
419 base_value_,
420 modulation_input_uuid_,
421 symbol_,
422 description_,
423 automatable_,
424 hidden_))
425};
426
427inline auto
428format_as (const ProcessorParameter::UniqueId &id)
429{
430 return type_safe::get (id).view ();
431}
432
433using ProcessorParameterPtrVariant = std::variant<ProcessorParameter *>;
434}
435
436DEFINE_UUID_HASH_SPECIALIZATION (zrythm::dsp::ProcessorParameter::Uuid)
437
438namespace zrythm::dsp
439{
446class ProcessorParameterRegistry
447 : public utils::
448 OwningObjectRegistry<ProcessorParameterPtrVariant, ProcessorParameter>
449{
450public:
451 ProcessorParameterRegistry (
452 dsp::PortRegistry &port_registry,
453 QObject * parent = nullptr)
455 ProcessorParameterPtrVariant,
456 ProcessorParameter> (parent),
457 port_registry_ (port_registry)
458 {
459 }
460
462 find_by_unique_id (const ProcessorParameter::UniqueId &id) const
463 {
464 const auto &map = get_hash_map ();
465 for (const auto &kv : map)
466 {
467 if (std::get<ProcessorParameter *> (kv.second)->get_unique_id () == id)
468 {
469 return std::get<ProcessorParameter *> (kv.second);
470 }
471 }
472 return nullptr;
473 }
474
476 find_by_unique_id_or_throw (const ProcessorParameter::UniqueId &id) const
477 {
478 auto * val = find_by_unique_id (id);
479 if (val == nullptr) [[unlikely]]
480 {
481 throw std::runtime_error (
482 fmt::format ("Processor Parameter with unique id {} not found", id));
483 }
484 return val;
485 }
486
487private:
488 struct ProcessorParameterRegistryBuilder
489 {
490 ProcessorParameterRegistryBuilder (dsp::PortRegistry &port_registry)
491 : port_registry_ (port_registry)
492 {
493 }
494
495 template <typename T> std::unique_ptr<T> build () const
496 {
497 return std::make_unique<T> (
498 port_registry_, ProcessorParameter::UniqueId (u8""), ParameterRange{},
499 u8"");
500 }
501
502 dsp::PortRegistry &port_registry_;
503 };
504
505 friend void
506 from_json (const nlohmann::json &j, ProcessorParameterRegistry &reg)
507 {
508 from_json_with_builder (
509 j, reg, ProcessorParameterRegistryBuilder{ reg.port_registry_ });
510 }
511
512private:
513 dsp::PortRegistry &port_registry_;
514};
515
516using ProcessorParameterUuidReference = utils::UuidReference<
518
519} // namespace zrythm::dsp
520
521namespace std
522{
523template <> struct hash<zrythm::dsp::ProcessorParameter::UniqueId>
524{
525 size_t operator() (const zrythm::dsp::ProcessorParameter::UniqueId &id) const
526 {
527 return id.hash ();
528 }
529};
530}
Interface for transport.
Definition itransport.h:16
float minf_
Minimum, maximum and zero values for this parameter.
Definition parameter.h:133
float zerof_
The zero position of the port.
Definition parameter.h:141
Unit unit_
Parameter unit.
Definition parameter.h:128
@ GainAmplitude
Parameter is a gain amplitude (0-2.0).
Definition parameter.h:50
@ Enumeration
Port's only reasonable values are its scale points.
Definition parameter.h:56
@ Logarithmic
Logarithmic-scaled float parameter.
Definition parameter.h:53
@ Linear
Linearly-scaled float parameter.
Definition parameter.h:32
@ Integer
Whether the port is an integer.
Definition parameter.h:42
@ Toggle
Whether the port is a toggle (on/off).
Definition parameter.h:39
@ Trigger
Trigger parameters are set to on to trigger a change during processing and then turned off at the end...
Definition parameter.h:64
Unit
Unit to be displayed in the UI.
Definition parameter.h:72
float deff_
Default value.
Definition parameter.h:146
Processor parameter that accepts automation and modulation sources and integrates with QML and the DS...
Definition parameter.h:167
std::function< std::optional< float >(units::sample_t sample_position)> AutomationValueProvider
Provides the automation value for a given sample position.
Definition parameter.h:216
void process_block(dsp::graph::EngineProcessTimeInfo time_nfo, const dsp::ITransport &transport, const dsp::TempoMap &tempo_map) noexcept override
Processes automation and modulation.
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().
Q_INVOKABLE float currentValue() const
Returns the current (normalized) value after any automation and modulation has been applied.
Definition parameter.h:248
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:256
Represents a node in a DSP graph.
Definition graph_node.h:160
Interface for objects that can be processed in the DSP graph.
Definition graph_node.h:86
A registry that owns and manages objects identified by a UUID.
Lightweight UTF-8 string wrapper with safe conversions.
Definition utf8_string.h:37
Base class for objects that need to be uniquely identified by UUID.
A reference-counted RAII wrapper for a UUID in a registry.
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