Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
track_processor.h
1// SPDX-FileCopyrightText: © 2019-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "dsp/port.h"
7#include "dsp/processor_base.h"
8#include "structure/arrangement/arranger_object_all.h"
9#include "structure/arrangement/timeline_data_provider.h"
10#include "structure/tracks/clip_playback_data_provider.h"
11#include "utils/icloneable.h"
12#include "utils/mpmc_queue.h"
13#include "utils/types.h"
14
15#include <farbot/RealtimeObject.hpp>
16
17namespace zrythm::structure::tracks
18{
25class TrackProcessor final : public QObject, public dsp::ProcessorBase
26{
27 Q_OBJECT
28 QML_ELEMENT
29 QML_UNCREATABLE ("")
30
31 using PortType = dsp::PortType;
32 using PortFlow = dsp::PortFlow;
33
34 struct TrackProcessorProcessingCaches
35 {
36 std::vector<dsp::AudioPort *> audio_ins_rt_;
37 std::vector<dsp::AudioPort *> audio_outs_rt_;
38 dsp::MidiPort * midi_in_rt_{};
39 dsp::MidiPort * midi_out_rt_{};
40 dsp::MidiPort * piano_roll_rt_{};
41 dsp::ProcessorParameter * mono_param_{};
42 dsp::ProcessorParameter * input_gain_{};
43 dsp::ProcessorParameter * output_gain_{};
44 dsp::ProcessorParameter * monitor_audio_{};
45 };
46
47public:
48 using StereoPortPair = std::pair<std::span<float>, std::span<float>>;
49 using ConstStereoPortPair =
50 std::pair<std::span<const float>, std::span<const float>>;
51
59 using FillEventsCallback = std::function<void (
60 const dsp::ITransport &transport,
61 const EngineProcessTimeInfo &time_nfo,
62 dsp::MidiEventVector * midi_events,
63 std::optional<StereoPortPair> stereo_ports)>;
64
71 using HandleRecordingCallback = std::function<void (
72 const EngineProcessTimeInfo &time_nfo,
73 const dsp::MidiEventVector * midi_events,
74 std::optional<ConstStereoPortPair> stereo_ports)>;
75
85 using AppendMidiInputsToOutputsFunc = std::function<void (
86 dsp::MidiEventVector &out_events,
87 const dsp::MidiEventVector &in_events,
88 const EngineProcessTimeInfo &time_nfo)>;
89
95 using TransformMidiInputsFunc = std::function<void (dsp::MidiEventVector &)>;
96
102 using EnabledProvider = std::function<bool ()>;
103
104 using TrackNameProvider = std::function<utils::Utf8String ()>;
105
106 using MidiEventProviderProcessFunc = std::function<void (
107 const EngineProcessTimeInfo &time_nfo,
108 dsp::MidiEventVector &output_buffer)>;
109
110 enum class ActiveMidiEventProviders : uint8_t
111 {
112 Timeline = 1 << 0,
113 ClipLauncher = 1 << 1,
114 PianoRoll = 1 << 2,
115 Recording = 1 << 3,
116 Custom = 1 << 4,
117 };
118
119 enum class ActiveAudioProviders : uint8_t
120 {
121 Timeline = 1 << 0,
122 ClipLauncher = 1 << 1,
123 Recording = 1 << 2,
124 Custom = 1 << 3,
125 };
126
135 const dsp::TempoMap &tempo_map,
136 PortType signal_type,
137 TrackNameProvider track_name_provider,
138 EnabledProvider enabled_provider,
139 bool generates_midi_events,
140 bool has_midi_cc,
141 bool is_audio_track,
142 ProcessorBaseDependencies dependencies,
143 std::optional<FillEventsCallback> fill_events_cb = std::nullopt,
144 std::optional<TransformMidiInputsFunc> transform_midi_inputs_func =
145 std::nullopt,
146 std::optional<AppendMidiInputsToOutputsFunc>
147 append_midi_inputs_to_outputs_func = std::nullopt,
148 QObject * parent = nullptr);
149
150 // note: this should eventually be passed from the constructor
151 void set_handle_recording_callback (HandleRecordingCallback handle_rec_cb)
152 {
153 handle_recording_cb_ = std::move (handle_rec_cb);
154 }
155
156 bool is_audio () const { return is_audio_; }
157
158 constexpr bool is_midi () const { return is_midi_; }
159
160 utils::Utf8String get_full_designation_for_port (const dsp::Port &port) const;
161
162 // ============================================================================
163 // ProcessorBase Interface
164 // ============================================================================
165
180 EngineProcessTimeInfo time_nfo,
181 const dsp::ITransport &transport) noexcept override;
182
183 void custom_prepare_for_processing (
184 const dsp::graph::GraphNode * node,
185 units::sample_rate_t sample_rate,
186 nframes_t max_block_length) override;
187
188 void custom_release_resources () override;
189
190 // ============================================================================
191
192 dsp::AudioPort &get_stereo_in_port () const
193 {
194 assert (is_audio ());
195 return *get_input_ports ().front ().get_object_as<dsp::AudioPort> ();
196 }
197 dsp::AudioPort &get_stereo_out_port () const
198 {
199 assert (is_audio ());
200 return *get_output_ports ().front ().get_object_as<dsp::AudioPort> ();
201 }
202
203 dsp::ProcessorParameter &get_mono_param () const
204 {
205 return *std::get<dsp::ProcessorParameter *> (
206 dependencies ().param_registry_.find_by_id_or_throw (*mono_id_));
207 }
208 dsp::ProcessorParameter &get_input_gain_param () const
209 {
210 return *std::get<dsp::ProcessorParameter *> (
211 dependencies ().param_registry_.find_by_id_or_throw (*input_gain_id_));
212 }
213 dsp::ProcessorParameter &get_output_gain_param () const
214 {
215 return *std::get<dsp::ProcessorParameter *> (
216 dependencies ().param_registry_.find_by_id_or_throw (*output_gain_id_));
217 }
218 dsp::ProcessorParameter &get_monitor_audio_param () const
219 {
220 return *std::get<dsp::ProcessorParameter *> (
221 dependencies ().param_registry_.find_by_id_or_throw (*monitor_audio_id_));
222 }
223
230 dsp::ProcessorParameter &
232 {
233 assert (has_midi_cc_);
234 return *std::get<dsp::ProcessorParameter *> (
235 dependencies ().param_registry_.find_by_id_or_throw (
236 midi_cc_caches_->midi_cc_ids_.at ((channel * 128) + control_no)));
237 }
238
248 {
249 assert (is_midi ());
250 return *get_input_ports ().front ().get_object_as<dsp::MidiPort> ();
251 }
252
257 {
258 assert (is_midi ());
259 return *get_output_ports ().front ().get_object_as<dsp::MidiPort> ();
260 }
261
269 {
270 return *get_input_ports ().at (1).get_object_as<dsp::MidiPort> ();
271 }
272
273 auto &timeline_audio_data_provider ()
274 {
275 return *timeline_audio_data_provider_;
276 }
277 auto &timeline_midi_data_provider () { return *timeline_midi_data_provider_; }
278
279 auto &clip_playback_data_provider () { return *clip_playback_data_provider_; }
280
290 ActiveMidiEventProviders event_providers,
291 bool active);
292
301 void
302 set_audio_providers_active (ActiveAudioProviders audio_providers, bool active);
303
307 void
308 set_custom_midi_event_provider (MidiEventProviderProcessFunc process_func);
309
320 const EngineProcessTimeInfo &time_nfo,
321 const dsp::ITransport &transport,
322 dsp::MidiEventVector &midi_events);
323
333 const EngineProcessTimeInfo &time_nfo,
334 const dsp::ITransport &transport,
335 StereoPortPair stereo_ports);
336
337private:
338 friend void to_json (nlohmann::json &j, const TrackProcessor &tp)
339 {
340 to_json (j, static_cast<const dsp::ProcessorBase &> (tp));
341 }
342 friend void from_json (const nlohmann::json &j, TrackProcessor &tp);
343
344 friend void init_from (
345 TrackProcessor &obj,
346 const TrackProcessor &other,
347 utils::ObjectCloneType clone_type);
348
353 void handle_recording (
354 const EngineProcessTimeInfo &time_nfo,
355 const dsp::ITransport &transport);
356
360 [[gnu::hot]] void add_events_from_midi_cc_control_ports (
361 dsp::MidiEventVector &events,
362 nframes_t local_offset);
363
367 void set_param_id_caches ();
368
369// TODO
370#if 0
371 void set_midi_mappings ();
372#endif
373
374private:
375 const bool is_midi_;
376 const bool is_audio_;
377 const bool has_piano_roll_port_;
378 const bool has_midi_cc_;
379
380 const EnabledProvider enabled_provider_;
381 const TrackNameProvider track_name_provider_;
382
383 std::optional<FillEventsCallback> fill_events_cb_;
384 std::optional<HandleRecordingCallback> handle_recording_cb_;
385 AppendMidiInputsToOutputsFunc append_midi_inputs_to_outputs_func_;
386 std::optional<TransformMidiInputsFunc> transform_midi_inputs_func_;
387
389 std::optional<dsp::ProcessorParameter::Uuid> mono_id_;
390
392 std::optional<dsp::ProcessorParameter::Uuid> input_gain_id_;
393
399 std::optional<dsp::ProcessorParameter::Uuid> output_gain_id_;
400
410 std::optional<dsp::ProcessorParameter::Uuid> monitor_audio_id_;
411
412#if 0
414 std::unique_ptr<engine::session::MidiMappings> cc_mappings_;
415#endif
416
420 struct MidiCcCaches
421 {
423 std::array<dsp::ProcessorParameter::Uuid, 16_zu * 128> midi_cc_ids_;
424
425 using SixteenPortUuidArray = std::array<dsp::ProcessorParameter::Uuid, 16>;
426
428 SixteenPortUuidArray pitch_bend_ids_;
429
439 SixteenPortUuidArray poly_key_pressure_ids_;
440
447 SixteenPortUuidArray channel_pressure_ids_;
448
455 MPMCQueue<dsp::ProcessorParameter *> updated_midi_automatable_ports_;
456 };
457
458 std::unique_ptr<MidiCcCaches> midi_cc_caches_;
459
460 // Processing caches
461 std::unique_ptr<TrackProcessorProcessingCaches> processing_caches_;
462
466 std::unique_ptr<arrangement::MidiTimelineDataProvider>
467 timeline_midi_data_provider_;
468
472 std::unique_ptr<arrangement::AudioTimelineDataProvider>
473 timeline_audio_data_provider_;
474
475 std::unique_ptr<ClipPlaybackDataProvider> clip_playback_data_provider_;
476
477 // TODO: piano roll, recording data providers
478
479 farbot::RealtimeObject<
480 std::optional<MidiEventProviderProcessFunc>,
481 farbot::RealtimeObjectOptions::nonRealtimeMutatable>
482 custom_midi_event_provider_;
483
484 std::atomic<ActiveMidiEventProviders> active_midi_event_providers_;
485 std::atomic<ActiveAudioProviders> active_audio_providers_;
486
487 static_assert (decltype (active_midi_event_providers_)::is_always_lock_free);
488};
489}
490
491ENUM_ENABLE_BITSET (
492 zrythm::structure::tracks::TrackProcessor::ActiveMidiEventProviders);
Multiple Producer Multiple Consumer lock-free queue.
Definition mpmc_queue.h:65
Audio port specifics.
Definition audio_port.h:25
Interface for transport.
Definition itransport.h:17
A lock-free thread-safe vector of MidiEvents.
Definition midi_event.h:78
MIDI port specifics.
Definition midi_port.h:22
A base class for ports used for connecting processors in the DSP graph.
Definition port.h:30
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
Represents a node in a DSP graph.
Definition graph_node.h:129
A TrackProcessor is a standalone processor that is used as the first step when processing a track in ...
void fill_midi_events(const EngineProcessTimeInfo &time_nfo, const dsp::ITransport &transport, dsp::MidiEventVector &midi_events)
Wrapper for MIDI/instrument/chord tracks to fill in MidiEvents from the timeline data.
std::function< void( const EngineProcessTimeInfo &time_nfo, const dsp::MidiEventVector * midi_events, std::optional< ConstStereoPortPair > stereo_ports)> HandleRecordingCallback
Callback to record the given audio or MIDI data.
void custom_process_block(EngineProcessTimeInfo time_nfo, const dsp::ITransport &transport) noexcept override
Process the TrackProcessor.
void fill_audio_events(const EngineProcessTimeInfo &time_nfo, const dsp::ITransport &transport, StereoPortPair stereo_ports)
Wrapper for audio tracks to fill in StereoPorts from the timeline data.
dsp::MidiPort & get_piano_roll_port() const
MIDI input for receiving MIDI signals from the piano roll (i.e., MIDI notes inside regions) or other ...
std::function< void( dsp::MidiEventVector &out_events, const dsp::MidiEventVector &in_events, const EngineProcessTimeInfo &time_nfo)> AppendMidiInputsToOutputsFunc
Custom logic to use when appending the MIDI input events to the output events.
dsp::MidiPort & get_midi_out_port() const
MIDI out port, if MIDI.
std::function< void( const dsp::ITransport &transport, const EngineProcessTimeInfo &time_nfo, dsp::MidiEventVector * midi_events, std::optional< StereoPortPair > stereo_ports)> FillEventsCallback
Function called during processing to fill events.
TrackProcessor(const dsp::TempoMap &tempo_map, PortType signal_type, TrackNameProvider track_name_provider, EnabledProvider enabled_provider, bool generates_midi_events, bool has_midi_cc, bool is_audio_track, ProcessorBaseDependencies dependencies, std::optional< FillEventsCallback > fill_events_cb=std::nullopt, std::optional< TransformMidiInputsFunc > transform_midi_inputs_func=std::nullopt, std::optional< AppendMidiInputsToOutputsFunc > append_midi_inputs_to_outputs_func=std::nullopt, QObject *parent=nullptr)
Creates a new track processor for the given track.
void set_audio_providers_active(ActiveAudioProviders audio_providers, bool active)
Used to enable or disable audio providers.
dsp::ProcessorParameter & get_midi_cc_param(midi_byte_t channel, midi_byte_t control_no)
std::function< void(dsp::MidiEventVector &)> TransformMidiInputsFunc
Function to transform the given MIDI inputs.
void set_midi_providers_active(ActiveMidiEventProviders event_providers, bool active)
Used to enable or disable MIDI event providers.
void set_custom_midi_event_provider(MidiEventProviderProcessFunc process_func)
Replaces the "Custom" MIDI event provider.
std::function< bool()> EnabledProvider
Function that returns if the track for this processor is enabled.
dsp::MidiPort & get_midi_in_port() const
MIDI in Port.
Lightweight UTF-8 string wrapper with safe conversions.
Definition utf8_string.h:38
uint32_t nframes_t
Frame count.
Definition types.h:58
uint8_t midi_byte_t
MIDI byte.
Definition types.h:55
Common struct to pass around during processing to avoid repeating the data in function arguments.
Definition types.h:133