Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
fader.h
1// SPDX-FileCopyrightText: © 2019-2022, 2024-2026 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "dsp/processor_base.h"
7#include "utils/icloneable.h"
8
9namespace zrythm::dsp
10{
14class Fader : public QObject, public dsp::ProcessorBase
15{
16 Q_OBJECT
17 Q_PROPERTY (
18 MidiFaderMode midiMode READ midiMode WRITE setMidiMode NOTIFY midiModeChanged)
19 Q_PROPERTY (zrythm::dsp::ProcessorParameter * gain READ gain CONSTANT)
20 Q_PROPERTY (zrythm::dsp::ProcessorParameter * balance READ balance CONSTANT)
21 Q_PROPERTY (zrythm::dsp::ProcessorParameter * mute READ mute CONSTANT)
22 Q_PROPERTY (zrythm::dsp::ProcessorParameter * solo READ solo CONSTANT)
23 Q_PROPERTY (zrythm::dsp::ProcessorParameter * listen READ listen CONSTANT)
24 Q_PROPERTY (
25 zrythm::dsp::ProcessorParameter * monoToggle READ monoToggle CONSTANT)
26 Q_PROPERTY (
27 zrythm::dsp::ProcessorParameter * swapPhaseToggle READ swapPhaseToggle
28 CONSTANT)
29 QML_ELEMENT
30 QML_UNCREATABLE ("")
31
32 struct FaderProcessingCaches
33 {
34 dsp::ProcessorParameter * amp_param_{};
35 dsp::ProcessorParameter * balance_param_{};
36 dsp::ProcessorParameter * mute_param_{};
37 dsp::ProcessorParameter * solo_param_{};
38 dsp::ProcessorParameter * listen_param_{};
39 dsp::ProcessorParameter * mono_compat_enabled_param_{};
40 dsp::ProcessorParameter * swap_phase_param_{};
41 std::vector<dsp::AudioPort *> audio_ins_rt_;
42 std::vector<dsp::AudioPort *> audio_outs_rt_;
43 dsp::MidiPort * midi_in_rt_{};
44 dsp::MidiPort * midi_out_rt_{};
45 };
46
47public:
48 enum class MidiFaderMode : uint8_t
49 {
52
53
55 };
56 Q_ENUM (MidiFaderMode)
57
58public:
68 using ShouldBeMutedCallback = std::function<bool (bool fader_solo_status)>;
69
74 using MuteGainCallback = std::function<float ()>;
75
82 using PreProcessAudioCallback = std::function<void (
83 std::pair<std::span<float>, std::span<float>> stereo_bufs,
84 const dsp::graph::EngineProcessTimeInfo &time_nfo)>;
85
95 Fader (
97 dsp::PortType signal_type,
98 bool hard_limit_output,
99 bool make_params_automatable,
100 std::optional<std::function<utils::Utf8String ()>> owner_name_provider,
101 ShouldBeMutedCallback should_be_muted_cb,
102 QObject * parent = nullptr);
103
104 // ============================================================================
105 // QML Interface
106 // ============================================================================
107
108 MidiFaderMode midiMode () const { return midi_mode_; }
109 void setMidiMode (MidiFaderMode mode)
110 {
111 if (midi_mode_ == mode)
112 return;
113
114 midi_mode_ = mode;
115 Q_EMIT midiModeChanged (mode);
116 }
117 Q_SIGNAL void midiModeChanged (MidiFaderMode mode);
118
119 zrythm::dsp::ProcessorParameter * gain () const
120 {
121 if (amp_id_.has_value ())
122 {
123 return &get_amp_param ();
124 }
125 return nullptr;
126 }
127 zrythm::dsp::ProcessorParameter * balance () const
128 {
129 if (balance_id_.has_value ())
130 {
131 return &get_balance_param ();
132 }
133 return nullptr;
134 }
135 zrythm::dsp::ProcessorParameter * mute () const
136 {
137 if (mute_id_.has_value ())
138 {
139 return &get_mute_param ();
140 }
141 return nullptr;
142 }
143 zrythm::dsp::ProcessorParameter * solo () const
144 {
145 if (solo_id_.has_value ())
146 {
147 return &get_solo_param ();
148 }
149 return nullptr;
150 }
151 zrythm::dsp::ProcessorParameter * listen () const
152 {
153 if (listen_id_.has_value ())
154 {
155 return &get_listen_param ();
156 }
157 return nullptr;
158 }
159 zrythm::dsp::ProcessorParameter * monoToggle () const
160 {
161 if (mono_compat_enabled_id_.has_value ())
162 {
163 return &get_mono_compat_enabled_param ();
164 }
165 return nullptr;
166 }
167 zrythm::dsp::ProcessorParameter * swapPhaseToggle () const
168 {
169 if (swap_phase_id_.has_value ())
170 {
171 return &get_swap_phase_param ();
172 }
173 return nullptr;
174 }
175
176 // ============================================================================
177
178 /**
179 * Returns if the fader is muted.
180 */
181 bool currently_muted () const
182 {
183 const auto &mute_param = get_mute_param ();
184 return mute_param.range ().is_toggled (mute_param.currentValue ());
185 }
186
187 /**
188 * Returns if the track is soloed.
189 */
190 [[gnu::hot]] bool currently_soloed () const
191 {
192 const auto &solo_param = get_solo_param ();
193 return solo_param.range ().is_toggled (solo_param.currentValue ());
194 }
195
196 /**
197 * Returns whether the fader is listened.
198 */
199 bool currently_listened () const
200 {
201 const auto &listened_param = get_listen_param ();
202 return listened_param.range ().is_toggled (listened_param.currentValue ());
203 }
204
205 /**
206 * Gets the fader amplitude (not db)
207 */
208 float get_current_amp () const
209 {
210 const auto &amp_param = get_amp_param ();
211 return amp_param.range ().convertFrom0To1 (amp_param.currentValue ());
212 }
213
214 std::string db_string_getter () const;
215
216 void set_mute_gain_callback (MuteGainCallback cb)
217 {
218 mute_gain_cb_ = std::move (cb);
219 }
220
221 void set_preprocess_audio_callback (PreProcessAudioCallback cb)
222 {
223 preprocess_audio_cb_ = std::move (cb);
224 }
225
226 // ============================================================================
227 // ProcessorBase Interface
228 // ============================================================================
229
230 void custom_prepare_for_processing (
231 const graph::GraphNode * node,
232 units::sample_rate_t sample_rate,
233 units::sample_u32_t max_block_length) override;
235 void custom_release_resources () override;
236
237 [[gnu::hot]] void custom_process_block (
239 const dsp::ITransport &transport,
240 const dsp::TempoMap &tempo_map) noexcept override;
241
242 // ============================================================================
243
244 bool is_audio () const { return signal_type_ == dsp::PortType::Audio; }
245 bool is_midi () const { return signal_type_ == dsp::PortType::Midi; }
246
247 bool hard_limiting_enabled () const { return hard_limit_output_; }
248
249 friend void
250 init_from (Fader &obj, const Fader &other, utils::ObjectCloneType clone_type);
251
252 dsp::ProcessorParameter &get_amp_param () const
253 {
254 return *std::get<dsp::ProcessorParameter *> (
255 dependencies ().param_registry_.find_by_id_or_throw (*amp_id_));
256 }
257 dsp::ProcessorParameter &get_balance_param () const
258 {
259 return *std::get<dsp::ProcessorParameter *> (
260 dependencies ().param_registry_.find_by_id_or_throw (*balance_id_));
261 }
262 dsp::ProcessorParameter &get_mute_param () const
263 {
264 return *std::get<dsp::ProcessorParameter *> (
265 dependencies ().param_registry_.find_by_id_or_throw (*mute_id_));
266 }
267 dsp::ProcessorParameter &get_solo_param () const
268 {
269 return *std::get<dsp::ProcessorParameter *> (
270 dependencies ().param_registry_.find_by_id_or_throw (*solo_id_));
271 }
272 dsp::ProcessorParameter &get_listen_param () const
273 {
274 return *std::get<dsp::ProcessorParameter *> (
275 dependencies ().param_registry_.find_by_id_or_throw (*listen_id_));
276 }
277 dsp::ProcessorParameter &get_mono_compat_enabled_param () const
278 {
279 return *std::get<dsp::ProcessorParameter *> (
280 dependencies ().param_registry_.find_by_id_or_throw (
281 *mono_compat_enabled_id_));
282 }
283 dsp::ProcessorParameter &get_swap_phase_param () const
284 {
285 return *std::get<dsp::ProcessorParameter *> (
286 dependencies ().param_registry_.find_by_id_or_throw (*swap_phase_id_));
287 }
288 dsp::AudioPort &get_stereo_in_port () const
289 {
290 if (!is_audio ())
291 {
292 throw std::runtime_error ("Not an audio fader");
293 }
294 return *get_input_ports ().at (0).get_object_as<dsp::AudioPort> ();
295 }
296 dsp::AudioPort &get_stereo_out_port () const
297 {
298 if (!is_audio ())
299 {
300 throw std::runtime_error ("Not an audio fader");
301 }
302 return *get_output_ports ().at (0).get_object_as<dsp::AudioPort> ();
303 }
304 dsp::MidiPort &get_midi_in_port () const
305 {
306 return *get_input_ports ().front ().get_object_as<dsp::MidiPort> ();
307 }
308 dsp::MidiPort &get_midi_out_port () const
309 {
310 return *get_output_ports ().front ().get_object_as<dsp::MidiPort> ();
311 }
312
313 auto currently_soloed_rt () const noexcept [[clang::nonblocking]]
314 {
315 return processing_caches_->solo_param_->range ().is_toggled (
316 processing_caches_->solo_param_->currentValue ());
317 };
318
319 bool currently_listened_rt () const noexcept [[clang::nonblocking]]
320 {
321 const auto &listened_param = processing_caches_->listen_param_;
322 return listened_param->range ().is_toggled (listened_param->currentValue ());
323 }
324
325private:
326 static constexpr auto kMidiModeKey = "midiMode"sv;
327 friend void to_json (nlohmann::json &j, const Fader &fader);
328 friend void from_json (const nlohmann::json &j, Fader &fader);
329
336 void init_param_caches ();
337
342 float calculate_target_gain_rt () const;
343
344 bool effectively_muted () const
345 {
346 return currently_muted () || should_be_muted_cb_ (currently_soloed ());
347 }
348
349 bool effectively_muted_rt () const
350 {
351 const auto currently_muted_rt = [this] () {
352 return processing_caches_->mute_param_->range ().is_toggled (
353 processing_caches_->mute_param_->currentValue ());
354 };
355
356 return currently_muted_rt () || should_be_muted_cb_ (currently_soloed_rt ());
357 }
358
359private:
360 dsp::PortType signal_type_;
361
362 bool hard_limit_output_{};
363
371 float last_cc_volume_ = 0.f;
372
376 std::optional<dsp::ProcessorParameter::Uuid> amp_id_;
377
379 std::optional<dsp::ProcessorParameter::Uuid> balance_id_;
380
384 std::optional<dsp::ProcessorParameter::Uuid> mute_id_;
385
387 std::optional<dsp::ProcessorParameter::Uuid> solo_id_;
388
390 std::optional<dsp::ProcessorParameter::Uuid> listen_id_;
391
393 std::optional<dsp::ProcessorParameter::Uuid> mono_compat_enabled_id_;
394
396 std::optional<dsp::ProcessorParameter::Uuid> swap_phase_id_;
397
400
408 juce::SmoothedValue<float> current_gain_{ 0.f };
409
410 ShouldBeMutedCallback should_be_muted_cb_;
411
412 std::optional<PreProcessAudioCallback> preprocess_audio_cb_;
413
414 MuteGainCallback mute_gain_cb_ = [] () { return 0.f; };
415
416 // Processor caches
417 std::unique_ptr<FaderProcessingCaches> processing_caches_;
418};
419}
void custom_process_block(dsp::graph::EngineProcessTimeInfo time_nfo, const dsp::ITransport &transport, const dsp::TempoMap &tempo_map) noexcept override
Custom processor logic after processing all owned parameters.
std::function< void( std::pair< std::span< float >, std::span< float > > stereo_bufs, const dsp::graph::EngineProcessTimeInfo &time_nfo)> PreProcessAudioCallback
Callback to pre-process the incoming audio before applying gain, pan, etc.
Definition fader.h:79
bool currently_soloed() const
Returns if the track is soloed.
Definition fader.h:187
@ MIDI_FADER_MODE_CC_VOLUME
Send CC volume event on change TODO.
Definition fader.h:51
@ MIDI_FADER_MODE_VEL_MULTIPLIER
Multiply velocity of all MIDI note ons.
Definition fader.h:48
float get_current_amp() const
Gets the fader amplitude (not db).
Definition fader.h:205
Fader(dsp::ProcessorBase::ProcessorBaseDependencies dependencies, dsp::PortType signal_type, bool hard_limit_output, bool make_params_automatable, std::optional< std::function< utils::Utf8String()> > owner_name_provider, ShouldBeMutedCallback should_be_muted_cb, QObject *parent=nullptr)
Creates a new fader.
bool currently_muted() const
Returns if the fader is muted.
Definition fader.h:178
std::function< float()> MuteGainCallback
Callback to get the gain that should be used if the fader is effectively muted.
Definition fader.h:71
std::function< bool(bool fader_solo_status)> ShouldBeMutedCallback
A callback to check if the fader should be muted based on various external factors (such as solo stat...
Definition fader.h:65
bool currently_listened() const
Returns whether the fader is listened.
Definition fader.h:196
Interface for transport.
Definition itransport.h:16
MIDI port specifics.
Definition midi_port.h:22
A base class for processors in the DSP graph.
Processor parameter that accepts automation and modulation sources and integrates with QML and the DS...
Definition parameter.h:167
Lightweight UTF-8 string wrapper with safe conversions.
Definition utf8_string.h:37
Common struct to pass around during processing to avoid repeating the data in function arguments.
Definition graph_node.h:51