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 <atomic>
7
8#include "dsp/processor_base.h"
9#include "utils/icloneable.h"
10
11namespace zrythm::dsp
12{
16class Fader : public QObject, public dsp::ProcessorBase
17{
18 Q_OBJECT
19 Q_PROPERTY (
20 MidiFaderMode midiMode READ midiMode WRITE setMidiMode NOTIFY midiModeChanged)
21 Q_PROPERTY (zrythm::dsp::ProcessorParameter * gain READ gain CONSTANT)
22 Q_PROPERTY (zrythm::dsp::ProcessorParameter * balance READ balance CONSTANT)
23 Q_PROPERTY (zrythm::dsp::ProcessorParameter * mute READ mute CONSTANT)
24 Q_PROPERTY (zrythm::dsp::ProcessorParameter * solo READ solo CONSTANT)
25 Q_PROPERTY (zrythm::dsp::ProcessorParameter * listen READ listen CONSTANT)
26 Q_PROPERTY (
27 zrythm::dsp::ProcessorParameter * monoToggle READ monoToggle CONSTANT)
28 Q_PROPERTY (
29 zrythm::dsp::ProcessorParameter * swapPhaseToggle READ swapPhaseToggle
30 CONSTANT)
31 QML_ELEMENT
32 QML_UNCREATABLE ("")
33
34 struct FaderProcessingCaches
35 {
36 dsp::ProcessorParameter * amp_param_{};
37 dsp::ProcessorParameter * balance_param_{};
38 dsp::ProcessorParameter * mute_param_{};
39 dsp::ProcessorParameter * solo_param_{};
40 dsp::ProcessorParameter * listen_param_{};
41 dsp::ProcessorParameter * mono_compat_enabled_param_{};
42 dsp::ProcessorParameter * swap_phase_param_{};
43 std::vector<dsp::AudioPort *> audio_ins_rt_;
44 std::vector<dsp::AudioPort *> audio_outs_rt_;
45 dsp::MidiPort * midi_in_rt_{};
46 dsp::MidiPort * midi_out_rt_{};
47 dsp::MidiEventBuffer midi_temp_buf_;
48 };
49
50public:
51 enum class MidiFaderMode : uint8_t
52 {
55
56
58 };
59 Q_ENUM (MidiFaderMode)
60
61public:
71 using ShouldBeMutedCallback = std::function<bool (bool fader_solo_status)>;
72
77 using MuteGainCallback = std::function<float ()>;
78
85 using PreProcessAudioCallback = std::function<void (
86 std::pair<std::span<float>, std::span<float>> stereo_bufs,
87 const dsp::graph::ProcessBlockInfo &time_nfo)>;
88
98 Fader (
99 utils::IObjectRegistry &registry,
100 dsp::PortType signal_type,
101 bool hard_limit_output,
102 bool make_params_automatable,
103 std::optional<std::function<utils::Utf8String ()>> owner_name_provider,
104 ShouldBeMutedCallback should_be_muted_cb,
105 QObject * parent = nullptr);
106
107 // ============================================================================
108 // QML Interface
109 // ============================================================================
110
111 MidiFaderMode midiMode () const { return midi_mode_.load (); }
112 void setMidiMode (MidiFaderMode mode)
113 {
114 if (midi_mode_.load () == mode)
115 return;
116
117 midi_mode_.store (mode);
118 Q_EMIT midiModeChanged (mode);
119 }
120 Q_SIGNAL void midiModeChanged (MidiFaderMode mode);
121
122 zrythm::dsp::ProcessorParameter * gain () const
123 {
124 if (amp_id_.has_value ())
125 {
126 return &get_amp_param ();
127 }
128 return nullptr;
129 }
130 zrythm::dsp::ProcessorParameter * balance () const
131 {
132 if (balance_id_.has_value ())
133 {
134 return &get_balance_param ();
135 }
136 return nullptr;
137 }
138 zrythm::dsp::ProcessorParameter * mute () const
139 {
140 if (mute_id_.has_value ())
141 {
142 return &get_mute_param ();
143 }
144 return nullptr;
145 }
146 zrythm::dsp::ProcessorParameter * solo () const
147 {
148 if (solo_id_.has_value ())
149 {
150 return &get_solo_param ();
151 }
152 return nullptr;
153 }
154 zrythm::dsp::ProcessorParameter * listen () const
155 {
156 if (listen_id_.has_value ())
157 {
158 return &get_listen_param ();
159 }
160 return nullptr;
161 }
162 zrythm::dsp::ProcessorParameter * monoToggle () const
163 {
164 if (mono_compat_enabled_id_.has_value ())
165 {
166 return &get_mono_compat_enabled_param ();
167 }
168 return nullptr;
169 }
170 zrythm::dsp::ProcessorParameter * swapPhaseToggle () const
171 {
172 if (swap_phase_id_.has_value ())
173 {
174 return &get_swap_phase_param ();
175 }
176 return nullptr;
177 }
178
179 // ============================================================================
180
181 /**
182 * Returns if the fader is muted.
183 */
184 bool currently_muted () const
185 {
186 const auto &mute_param = get_mute_param ();
187 return mute_param.range ().isToggled (mute_param.currentValue ());
188 }
189
190 /**
191 * Returns if the track is soloed.
192 */
193 [[gnu::hot]] bool currently_soloed () const
194 {
195 const auto &solo_param = get_solo_param ();
196 return solo_param.range ().isToggled (solo_param.currentValue ());
197 }
198
199 /**
200 * Returns whether the fader is listened.
201 */
202 bool currently_listened () const
203 {
204 const auto &listened_param = get_listen_param ();
205 return listened_param.range ().isToggled (listened_param.currentValue ());
206 }
207
208 /**
209 * Gets the fader amplitude (not db)
210 */
211 float get_current_amp () const
212 {
213 const auto &amp_param = get_amp_param ();
214 return amp_param.range ().convertFrom0To1 (amp_param.currentValue ());
215 }
216
217 std::string db_string_getter () const;
218
219 void set_mute_gain_callback (MuteGainCallback cb)
220 {
221 mute_gain_cb_ = std::move (cb);
222 }
223
224 void set_preprocess_audio_callback (PreProcessAudioCallback cb)
225 {
226 preprocess_audio_cb_ = std::move (cb);
227 }
228
229 // ============================================================================
230 // ProcessorBase Interface
231 // ============================================================================
232
233 void custom_prepare_for_processing (
234 const graph::GraphNode * node,
235 units::sample_rate_t sample_rate,
236 units::sample_u32_t max_block_length) override;
238 void custom_release_resources () override;
239
240 [[gnu::hot]] void custom_process_block (
242 const dsp::ITransport &transport,
243 const dsp::TempoMap &tempo_map) noexcept override;
244
245 // ============================================================================
246
247 bool is_audio () const { return signal_type_ == dsp::PortType::Audio; }
248 bool is_midi () const { return signal_type_ == dsp::PortType::Midi; }
249
250 bool hard_limiting_enabled () const { return hard_limit_output_; }
251
252 friend void
253 init_from (Fader &obj, const Fader &other, utils::ObjectCloneType clone_type);
254
255 dsp::ProcessorParameter &get_amp_param () const;
256 dsp::ProcessorParameter &get_balance_param () const;
257 dsp::ProcessorParameter &get_mute_param () const;
258 dsp::ProcessorParameter &get_solo_param () const;
259 dsp::ProcessorParameter &get_listen_param () const;
260 dsp::ProcessorParameter &get_mono_compat_enabled_param () const;
261 dsp::ProcessorParameter &get_swap_phase_param () const;
262 dsp::AudioPort &get_stereo_in_port () const
263 {
264 if (!is_audio ())
265 {
266 throw std::runtime_error ("Not an audio fader");
267 }
268 return *get_input_ports ().at (0).get_object_as<dsp::AudioPort> ();
269 }
270 dsp::AudioPort &get_stereo_out_port () const
271 {
272 if (!is_audio ())
273 {
274 throw std::runtime_error ("Not an audio fader");
275 }
276 return *get_output_ports ().at (0).get_object_as<dsp::AudioPort> ();
277 }
278 dsp::MidiPort &get_midi_in_port () const
279 {
280 return *get_input_ports ().front ().get_object_as<dsp::MidiPort> ();
281 }
282 dsp::MidiPort &get_midi_out_port () const
283 {
284 return *get_output_ports ().front ().get_object_as<dsp::MidiPort> ();
285 }
286
287 auto currently_soloed_rt () const noexcept [[clang::nonblocking]]
288 {
289 return processing_caches_->solo_param_->range ().isToggled (
290 processing_caches_->solo_param_->currentValue ());
291 };
292
293 bool currently_listened_rt () const noexcept [[clang::nonblocking]]
294 {
295 const auto &listened_param = processing_caches_->listen_param_;
296 return listened_param->range ().isToggled (listened_param->currentValue ());
297 }
298
299private:
300 static constexpr auto kMidiModeKey = "midiMode"sv;
301 friend void to_json (nlohmann::json &j, const Fader &fader);
302 friend void from_json (const nlohmann::json &j, Fader &fader);
303
310 void init_param_caches ();
311
316 float calculate_target_gain_rt () const;
317
318 bool effectively_muted () const
319 {
320 return currently_muted () || should_be_muted_cb_ (currently_soloed ());
321 }
322
323 bool effectively_muted_rt () const
324 {
325 const auto currently_muted_rt = [this] () {
326 return processing_caches_->mute_param_->range ().isToggled (
327 processing_caches_->mute_param_->currentValue ());
328 };
329
330 return currently_muted_rt () || should_be_muted_cb_ (currently_soloed_rt ());
331 }
332
333private:
334 dsp::PortType signal_type_;
335
336 bool hard_limit_output_{};
337
345 float last_cc_volume_ = 0.f;
346
350 std::optional<dsp::ProcessorParameter::Uuid> amp_id_;
351
353 std::optional<dsp::ProcessorParameter::Uuid> balance_id_;
354
358 std::optional<dsp::ProcessorParameter::Uuid> mute_id_;
359
361 std::optional<dsp::ProcessorParameter::Uuid> solo_id_;
362
364 std::optional<dsp::ProcessorParameter::Uuid> listen_id_;
365
367 std::optional<dsp::ProcessorParameter::Uuid> mono_compat_enabled_id_;
368
370 std::optional<dsp::ProcessorParameter::Uuid> swap_phase_id_;
371
372 std::atomic<MidiFaderMode> midi_mode_{
374 };
375
383 juce::SmoothedValue<float> current_gain_{ 0.f };
384
385 ShouldBeMutedCallback should_be_muted_cb_;
386
387 std::optional<PreProcessAudioCallback> preprocess_audio_cb_;
388
389 MuteGainCallback mute_gain_cb_ = [] () { return 0.f; };
390
391 // Processor caches
392 std::unique_ptr<FaderProcessingCaches> processing_caches_;
393};
394}
bool currently_soloed() const
Returns if the track is soloed.
Definition fader.h:190
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:82
@ MIDI_FADER_MODE_CC_VOLUME
Send CC volume event on change TODO.
Definition fader.h:54
@ MIDI_FADER_MODE_VEL_MULTIPLIER
Multiply velocity of all MIDI note ons.
Definition fader.h:51
float get_current_amp() const
Gets the fader amplitude (not db).
Definition fader.h:208
bool currently_muted() const
Returns if the fader is muted.
Definition fader.h:181
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:74
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:68
bool currently_listened() const
Returns whether the fader is listened.
Definition fader.h:199
Interface for transport.
Definition itransport.h:47
Packed contiguous buffer for RT MIDI events.
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