Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
region.h
1// SPDX-FileCopyrightText: © 2018-2022, 2024-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "dsp/position.h"
7#include "gui/dsp/arranger_object.h"
8#include "gui/dsp/automation_point.h"
9#include "gui/dsp/colored_object.h"
10#include "gui/dsp/loopable_object.h"
11#include "gui/dsp/muteable_object.h"
12#include "gui/dsp/named_object.h"
13#include "gui/dsp/timeline_object.h"
14#include "utils/format.h"
15
16class Track;
17
18class RegionLinkGroup;
19// class RegionOwner;
20class MidiNote;
21class MidiRegion;
22class ChordRegion;
23// class LaneOwnedObject;
24class ChordObject;
25class AudioRegion;
28
34
35#define DEFINE_REGION_QML_PROPERTIES(ClassType) \
36public: \
37 DEFINE_LOOPABLE_OBJECT_QML_PROPERTIES (ClassType) \
38 DEFINE_NAMEABLE_OBJECT_QML_PROPERTIES (ClassType) \
39 DEFINE_COLORED_OBJECT_QML_PROPERTIES (ClassType) \
40 /* ================================================================ */ \
41 /* helpers */ \
42 /* ================================================================ */
43
44template <typename RegionT> struct RegionChildType;
45
46template <> struct RegionChildType<MidiRegion>
47{
48 using type = MidiNote;
49};
50
51template <> struct RegionChildType<ChordRegion>
52{
53 using type = ChordObject;
54};
55
57{
58 using type = AutomationPoint;
59};
60
64
65// avoid errors
66template <> struct RegionChildType<AudioRegion>
67{
68 using type = DummyRegionOwnedObject;
69};
70
71template <typename RegionT>
72using RegionChildType_t = typename RegionChildType<RegionT>::type;
73
74template <typename RegionT>
76 !std::is_same_v<RegionChildType_t<RegionT>, DummyRegionOwnedObject>;
77
81enum class MusicalMode
82{
86 Off,
89};
90
91using RegionVariant =
92 std::variant<MidiRegion, ChordRegion, AutomationRegion, AudioRegion>;
93using RegionPtrVariant = to_pointer_variant<RegionVariant>;
94
95template <typename T>
97 std::same_as<T, MidiRegion> || std::same_as<T, ChordRegion>;
98
106class Region
107 : virtual public TimelineObject,
108 virtual public NamedObject,
109 virtual public MuteableObject,
110 virtual public LoopableObject,
111 public ColoredObject
112{
113public:
114 ~Region () override = default;
115
116 bool is_midi () const { return get_type () == Type::MidiRegion; }
117 bool is_audio () const { return get_type () == Type::AudioRegion; }
118 bool is_automation () const { return get_type () == Type::AutomationRegion; }
119 bool is_chord () const { return get_type () == Type::ChordRegion; }
120
125
126 bool has_link_group () const { return link_group_.has_value (); }
127
140 [[gnu::hot]] signed_frame_t
141 timeline_frames_to_local (signed_frame_t timeline_frames, bool normalize) const;
142
152 [[gnu::nonnull]] void get_frames_till_next_loop_or_end (
153 signed_frame_t timeline_frames,
154 signed_frame_t * ret_frames,
155 bool * is_loop) const;
156
161
166
177 static std::optional<ArrangerObjectPtrVariant> get_at_pos (
178 dsp::Position pos,
179 Track * track,
180 AutomationTrack * at,
181 bool include_region_end = false);
182
183protected:
184 Region (ArrangerObjectRegistry &object_registry)
185 : object_registry_ (object_registry)
186 {
187 }
188 Q_DISABLE_COPY_MOVE (Region)
189
190 void copy_members_from (const Region &other, ObjectCloneType clone_type);
191
192private:
193 static constexpr std::string_view kLinkGroupKey = "linkGroup";
194 friend void to_json (nlohmann::json &j, const Region &region)
195 {
196 j[kLinkGroupKey] = region.link_group_;
197 }
198 friend void from_json (const nlohmann::json &j, Region &region)
199 {
200 j.at (kLinkGroupKey).get_to (region.link_group_);
201 }
202
203public:
204 ArrangerObjectRegistry &object_registry_;
205
206 // FIXME: consider tracking link groups separately to decouple the link logic,
207 // region (probably) shouldn't know if it's in a link group
208 std::optional<int> link_group_;
209
215 int bounce_ = 0;
216
217 /* --- stretching related --- */
218
224 bool stretching_ = false;
225
227 double stretch_ratio_ = 1.0;
228
232 double before_length_ = 0.0;
233
234 /* --- stretching related end --- */
235};
236
240template <typename RegionT> class RegionImpl : virtual public Region
241{
242protected:
243 RegionImpl () noexcept { }
244
245public:
246 ~RegionImpl () override = default;
247 Z_DISABLE_COPY_MOVE (RegionImpl)
248
249 using RegionTPtr = RegionT *;
250
251 static constexpr bool is_midi ()
252 {
253 return std::is_same_v<RegionT, MidiRegion>;
254 }
255 static constexpr bool is_audio ()
256 {
257 return std::is_same_v<RegionT, AudioRegion>;
258 }
259 static constexpr bool is_automation ()
260 {
261 return std::is_same_v<RegionT, AutomationRegion>;
262 }
263 static constexpr bool is_chord ()
264 {
265 return std::is_same_v<RegionT, ChordRegion>;
266 }
267 static constexpr bool is_laned ()
268 {
269 return std::is_same_v<MidiRegion, RegionT>
270 || std::is_same_v<AudioRegion, RegionT>;
271 }
272
273 static constexpr bool has_children ()
274 {
275 return !std::is_same_v<RegionT, AudioRegion>;
276 }
277
278 using ChildT = RegionChildType_t<RegionT>;
279 using ChildTPtr = ChildT *;
280
284 void
285 append_children (this RegionT &self, std::vector<RegionOwnedObject *> &children)
287 {
288 for (auto * obj : self.get_children_view ())
289 {
290 children.push_back (obj);
291 }
292 }
293
314 TrackPtrVariant track_var,
315 const EngineProcessTimeInfo &time_nfo,
316 bool note_off_at_end,
317 bool is_note_off_for_loop_or_region_end,
318 dsp::MidiEventVector &midi_events) const
320
321 auto get_object_ptr (const ArrangerObject::Uuid id) const
323 {
324 return std::get<ChildT *> (
325 get_arranger_object_registry ().find_by_id_or_throw (id));
326 }
327 auto get_object_ptr (const ArrangerObjectUuidReference &id) const
329 {
330 return std::get<ChildT *> (id.get_object ());
331 }
332
333#if 0
334 auto * get_region_owner (this const RegionT &self)
335 {
336 if constexpr (is_laned ())
337 {
338 auto &lane = self.get_lane ();
339 return std::addressof (lane);
340 }
341 else if constexpr (is_automation ())
342 {
343 return std::visit (
344 [&] (auto &&automatable_track) -> AutomationTrack * {
345 using TrackT = base_type<decltype (automatable_track)>;
346 if constexpr (std::derived_from<TrackT, AutomatableTrack>)
347 {
348 auto * at =
349 automatable_track->get_automation_tracklist ()
350 .get_automation_track_by_port_id (self.automatable_port_id_);
351 return at;
352 }
353 else
354 {
355 throw std::runtime_error ("Invalid track");
356 }
357 },
358 self.get_track ());
359 }
360 else if constexpr (is_chord ())
361 {
362 return std::get<ChordTrack *> (self.get_track ());
363 }
364 }
365#endif
366
367 auto &get_arranger_object_registry () const { return object_registry_; }
368 auto &get_arranger_object_registry () { return object_registry_; }
369
373 void remove_all_children (this RegionT &self)
375 {
376 z_debug ("removing all children from {} ", self.get_name ());
377
378 auto vec = self.get_children_vector ();
379 for (auto &obj : vec)
380 {
381 self.remove_object (obj.id ());
382 }
383 }
384
385 bool get_muted (bool check_parent) const override;
386
387 ArrangerObjectPtrVariant add_clone_to_project (bool fire_events) const final
388 {
389 return insert_clone_to_project_at_index (-1, fire_events);
390 }
391
392 ArrangerObjectPtrVariant insert_clone_to_project () const final
393 {
394 return insert_clone_to_project_at_index (-1, true);
395 }
396
403 void set_link_group (int group_idx, bool update_identifier);
404
410 // void disconnect_region ();
411
416 void update_identifier (this RegionT &self)
417 {
418 /* reset link group */
419 // set_link_group (id_.link_group_, false);
420
421 // track_id_ = id_.track_uuid_;
422 if constexpr (RegionWithChildren<RegionT>)
423 {
424 for (auto * obj : self.get_children_view ())
425 {
426 obj->set_region_and_index (self);
427 }
428 }
429 }
430
438 static RegionT *
439 at_position (const Track * track, const AutomationTrack * at, dsp::Position pos);
440
444 void unlink ();
445
446 bool
447 are_members_valid (bool is_project, dsp::FramesPerTick frames_per_tick) const;
448
458 void stretch (double ratio);
459
460 void create_link_group_if_none ();
461
462private:
463 RegionTPtr
464 insert_clone_to_project_at_index (int index, bool fire_events) const;
465
478 [[gnu::hot]] void send_note_offs (
479 dsp::MidiEventVector &events,
480 EngineProcessTimeInfo time_nfo,
481 bool is_note_off_for_loop_or_region_end) const
483
484 const RegionT &get_derived () const
485 {
486 return static_cast<const RegionT &> (*this);
487 }
488 RegionT &get_derived () { return static_cast<RegionT &> (*this); }
489};
490
491template <typename RegionT>
493 std::derived_from<RegionImpl<RegionT>, Region> && FinalClass<RegionT>;
494
495DEFINE_ENUM_FORMATTER (
498 QT_TRANSLATE_NOOP_UTF8 ("MusicalMode", "Inherit"),
499 QT_TRANSLATE_NOOP_UTF8 ("MusicalMode", "Off"),
500 QT_TRANSLATE_NOOP_UTF8 ("MusicalMode", "On"));
501
503 return fmt::format (
504 "Region[id: {}, name: {}, "
505 "<{}> to <{}> ({} frames, {} ticks) - loop end <{}> - link group {}"
506 "]",
507 r.get_uuid (), r.get_name (), r.get_position (), *r.end_pos_,
508 r.end_pos_->frames_ - r.get_position ().frames_,
509 r.end_pos_->ticks_ - r.get_position ().ticks_, r.loop_end_pos_,
510 r.link_group_);
511})
512
513using RegionVariant =
514 std::variant<MidiRegion, ChordRegion, AutomationRegion, AudioRegion>;
515using RegionPtrVariant = to_pointer_variant<RegionVariant>;
516
517template <typename RegionT>
519
520extern template class RegionImpl<MidiRegion>;
521extern template class RegionImpl<AudioRegion>;
522extern template class RegionImpl<AutomationRegion>;
523extern template class RegionImpl<ChordRegion>;
524
auto get_position() const
Getter.
An AudioRegion represents a region of audio within a Track.
Interface for a track that has automatable parameters.
An automation point inside an AutomationTrack.
Represents an automation region, which contains a collection of automation points.
PositionProxy * end_pos_
End Position, if the object has one.
The ChordObject class represents a chord inside a ChordRegion.
dsp::Position loop_end_pos_
End position of the clip loop, relative to the object's start.
A MIDI note inside a Region shown in the piano roll.
Definition midi_note.h:31
A Region containing MIDI events.
Definition midi_region.h:43
utils::Utf8String get_name() const
Returns the name of the object.
ArrangerObjectPtrVariant add_clone_to_project(bool fire_events) const final
Appends the ArrangerObject to where it belongs in the project (eg, a Track), without taking into acco...
Definition region.h:387
void stretch(double ratio)
Stretch the region's contents.
void fill_midi_events(TrackPtrVariant track_var, const EngineProcessTimeInfo &time_nfo, bool note_off_at_end, bool is_note_off_for_loop_or_region_end, dsp::MidiEventVector &midi_events) const
Fills MIDI event queue from this MIDI or Chord region.
ArrangerObjectPtrVariant insert_clone_to_project() const final
Inserts the object where it belongs in the project (eg, a Track).
Definition region.h:392
void remove_all_children(this RegionT &self)
Removes all children objects from the region.
Definition region.h:373
static RegionT * at_position(const Track *track, const AutomationTrack *at, dsp::Position pos)
Returns the region at the given position in the given Track.
void append_children(this RegionT &self, std::vector< RegionOwnedObject * > &children)
Fills the given vector with all the children of this region.
Definition region.h:285
bool get_muted(bool check_parent) const override
Gets the mute status of the object.
void set_link_group(int group_idx, bool update_identifier)
Sets the link group to the region.
void unlink()
Removes the link group from the region, if any.
void update_identifier(this ChordRegion &self)
Definition region.h:416
A group of linked regions.
A region (clip) is an object on the timeline that contains either MidiNote's or AudioClip's.
Definition region.h:112
double stretch_ratio_
Used during arranger UI overlay actions.
Definition region.h:227
signed_frame_t timeline_frames_to_local(signed_frame_t timeline_frames, bool normalize) const
Converts frames on the timeline (global) to local frames (in the clip).
bool is_recording()
Returns if this region is currently being recorded onto.
static std::optional< ArrangerObjectPtrVariant > get_at_pos(dsp::Position pos, Track *track, AutomationTrack *at, bool include_region_end=false)
Returns the region at the given position, if any.
RegionLinkGroup * get_link_group()
Returns the region's link group.
int bounce_
Set to ON during bouncing if this region should be included.
Definition region.h:215
bool stretching_
Whether currently running the stretching algorithm.
Definition region.h:224
void get_frames_till_next_loop_or_end(signed_frame_t timeline_frames, signed_frame_t *ret_frames, bool *is_loop) const
Returns the number of frames until the next loop end point or the end of the region.
void update_link_group()
Updates all other regions in the region link group, if any.
double before_length_
The length before stretching, in ticks.
Definition region.h:232
Represents a track in the project.
Definition track.h:282
A lock-free thread-safe vector of MidiEvents.
Definition midi_event.h:76
Represents the position of an object.
Definition position.h:67
signed_frame_t frames_
Position in frames (samples).
Definition position.h:487
double ticks_
Precise total number of ticks.
Definition position.h:480
MusicalMode
Musical mode setting for audio regions.
Definition region.h:82
@ On
Musical mode on - auto-stretch when BPM changes.
Definition region.h:88
@ Inherit
Inherit from global musical mode setting.
Definition region.h:84
#define DEFINE_OBJECT_FORMATTER(obj_type, function_prefix, formatter_func)
Defines a formatter for the given object type.
Definition format.h:80
ObjectCloneType
Definition icloneable.h:25
int_fast64_t signed_frame_t
Signed type for frame index.
Definition types.h:82
Common struct to pass around during processing to avoid repeating the data in function arguments.
Definition types.h:176