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