Zrythm v2.0.0-alpha.1
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::ProcessBlockInfo &time_nfo)>;
85
95 Fader (
96 utils::IObjectRegistry &registry,
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 ().isToggled (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 ().isToggled (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 ().isToggled (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 dsp::ProcessorParameter &get_balance_param () const;
254 dsp::ProcessorParameter &get_mute_param () const;
255 dsp::ProcessorParameter &get_solo_param () const;
256 dsp::ProcessorParameter &get_listen_param () const;
257 dsp::ProcessorParameter &get_mono_compat_enabled_param () const;
258 dsp::ProcessorParameter &get_swap_phase_param () const;
259 dsp::AudioPort &get_stereo_in_port () const
260 {
261 if (!is_audio ())
262 {
263 throw std::runtime_error ("Not an audio fader");
264 }
265 return *get_input_ports ().at (0).get_object_as<dsp::AudioPort> ();
266 }
267 dsp::AudioPort &get_stereo_out_port () const
268 {
269 if (!is_audio ())
270 {
271 throw std::runtime_error ("Not an audio fader");
272 }
273 return *get_output_ports ().at (0).get_object_as<dsp::AudioPort> ();
274 }
275 dsp::MidiPort &get_midi_in_port () const
276 {
277 return *get_input_ports ().front ().get_object_as<dsp::MidiPort> ();
278 }
279 dsp::MidiPort &get_midi_out_port () const
280 {
281 return *get_output_ports ().front ().get_object_as<dsp::MidiPort> ();
282 }
283
284 auto currently_soloed_rt () const noexcept [[clang::nonblocking]]
285 {
286 return processing_caches_->solo_param_->range ().isToggled (
287 processing_caches_->solo_param_->currentValue ());
288 };
289
290 bool currently_listened_rt () const noexcept [[clang::nonblocking]]
291 {
292 const auto &listened_param = processing_caches_->listen_param_;
293 return listened_param->range ().isToggled (listened_param->currentValue ());
294 }
295
296private:
297 static constexpr auto kMidiModeKey = "midiMode"sv;
298 friend void to_json (nlohmann::json &j, const Fader &fader);
299 friend void from_json (const nlohmann::json &j, Fader &fader);
300
307 void init_param_caches ();
308
313 float calculate_target_gain_rt () const;
314
315 bool effectively_muted () const
316 {
317 return currently_muted () || should_be_muted_cb_ (currently_soloed ());
318 }
319
320 bool effectively_muted_rt () const
321 {
322 const auto currently_muted_rt = [this] () {
323 return processing_caches_->mute_param_->range ().isToggled (
324 processing_caches_->mute_param_->currentValue ());
325 };
326
327 return currently_muted_rt () || should_be_muted_cb_ (currently_soloed_rt ());
328 }
329
330private:
331 dsp::PortType signal_type_;
332
333 bool hard_limit_output_{};
334
342 float last_cc_volume_ = 0.f;
343
347 std::optional<dsp::ProcessorParameter::Uuid> amp_id_;
348
350 std::optional<dsp::ProcessorParameter::Uuid> balance_id_;
351
355 std::optional<dsp::ProcessorParameter::Uuid> mute_id_;
356
358 std::optional<dsp::ProcessorParameter::Uuid> solo_id_;
359
361 std::optional<dsp::ProcessorParameter::Uuid> listen_id_;
362
364 std::optional<dsp::ProcessorParameter::Uuid> mono_compat_enabled_id_;
365
367 std::optional<dsp::ProcessorParameter::Uuid> swap_phase_id_;
368
371
379 juce::SmoothedValue<float> current_gain_{ 0.f };
380
381 ShouldBeMutedCallback should_be_muted_cb_;
382
383 std::optional<PreProcessAudioCallback> preprocess_audio_cb_;
384
385 MuteGainCallback mute_gain_cb_ = [] () { return 0.f; };
386
387 // Processor caches
388 std::unique_ptr<FaderProcessingCaches> processing_caches_;
389};
390}
bool currently_soloed() const
Returns if the track is soloed.
Definition fader.h:187
std::function< void( std::pair< std::span< float >, std::span< float > > stereo_bufs, const dsp::graph::ProcessBlockInfo &time_nfo)> PreProcessAudioCallback
Callback to pre-process the incoming audio before applying gain, pan, etc.
Definition fader.h:79
@ 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
bool currently_muted() const
Returns if the fader is muted.
Definition fader.h:178
void custom_process_block(dsp::graph::ProcessBlockInfo time_nfo, const dsp::ITransport &transport, const dsp::TempoMap &tempo_map) noexcept override
Custom processor logic after processing all owned parameters.
Fader(utils::IObjectRegistry &registry, 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.
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:21
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:255
Abstract interface for a UUID-keyed object registry.
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