Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
loopable_object.h
1// SPDX-FileCopyrightText: © 2024-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "structure/arrangement/bounded_object.h"
7
8namespace zrythm::structure::arrangement
9{
10
11class ArrangerObjectLoopRange : public QObject
12{
13 Q_OBJECT
14 Q_PROPERTY (
15 zrythm::dsp::AtomicPositionQmlAdapter * clipStartPosition READ
16 clipStartPosition CONSTANT)
17 Q_PROPERTY (
18 zrythm::dsp::AtomicPositionQmlAdapter * loopStartPosition READ
19 loopStartPosition CONSTANT)
20 Q_PROPERTY (
21 zrythm::dsp::AtomicPositionQmlAdapter * loopEndPosition READ loopEndPosition
22 CONSTANT)
23 Q_PROPERTY (
24 bool trackBounds READ trackBounds WRITE setTrackBounds NOTIFY
25 trackBoundsChanged)
26 Q_PROPERTY (bool looped READ looped NOTIFY loopedChanged)
27 QML_ELEMENT
28 QML_UNCREATABLE ("")
29
30public:
31 ArrangerObjectLoopRange (
32 const ArrangerObjectBounds &bounds,
33 QObject * parent = nullptr);
34
39 int get_num_loops (bool count_incomplete) const;
40
41 // ========================================================================
42 // QML Interface
43 // ========================================================================
44
45 dsp::AtomicPositionQmlAdapter * clipStartPosition () const
46 {
47 return clip_start_pos_adapter_.get ();
48 }
49 dsp::AtomicPositionQmlAdapter * loopStartPosition () const
50 {
51 return loop_start_pos_adapter_.get ();
52 }
53 dsp::AtomicPositionQmlAdapter * loopEndPosition () const
54 {
55 return loop_end_pos_adapter_.get ();
56 }
57 bool trackBounds () const { return track_bounds_; }
58 void setTrackBounds (bool track)
59 {
60 if (track_bounds_ != track)
61 {
62 track_bounds_ = track;
63 Q_EMIT trackBoundsChanged (track);
64 }
65 }
66 Q_SIGNAL void trackBoundsChanged (bool track);
67
68 bool looped () const
69 {
70 return loopStartPosition ()->samples () > 0
71 || clipStartPosition ()->samples () > 0
72 || length ()->samples () != loopEndPosition ()->samples ();
73 }
74 Q_SIGNAL void loopedChanged ();
75
76 Q_SIGNAL void loopableObjectPropertiesChanged ();
77
78 // ========================================================================
79
82
83 units::precise_tick_t get_loop_length_in_ticks () const
84 {
85 return std::max (
86 units::ticks (0.0),
87 loop_end_pos_.get_ticks () - loop_start_pos_.get_ticks ());
88 }
89
92
93 units::sample_t get_loop_length_in_frames () const
94 {
95 return std::max (
96 units::sample_t (units::samples (0)),
97 loop_end_pos_.get_samples () - loop_start_pos_.get_samples ());
98 }
99
100private:
101 auto length () const -> const dsp::AtomicPositionQmlAdapter *
102 {
103 return bounds_.length ();
104 }
105
106 static constexpr auto kClipStartPosKey = "clipStartPos"sv;
107 static constexpr auto kLoopStartPosKey = "loopStartPos"sv;
108 static constexpr auto kLoopEndPosKey = "loopEndPos"sv;
109 static constexpr auto kTrackBoundsKey = "trackBounds"sv;
110 friend auto to_json (nlohmann::json &j, const ArrangerObjectLoopRange &object)
111 {
112 j[kClipStartPosKey] = object.clip_start_pos_;
113 j[kLoopStartPosKey] = object.loop_start_pos_;
114 j[kLoopEndPosKey] = object.loop_end_pos_;
115 j[kTrackBoundsKey] = object.track_bounds_;
116 }
117 friend auto
118 from_json (const nlohmann::json &j, ArrangerObjectLoopRange &object)
119 {
120 j.at (kClipStartPosKey).get_to (object.clip_start_pos_);
121 j.at (kLoopStartPosKey).get_to (object.loop_start_pos_);
122 j.at (kLoopEndPosKey).get_to (object.loop_end_pos_);
123 j.at (kTrackBoundsKey).get_to (object.track_bounds_);
124 Q_EMIT object.trackBoundsChanged (object.track_bounds_);
125 }
126
127 friend void init_from (
128 ArrangerObjectLoopRange &obj,
129 const ArrangerObjectLoopRange &other,
130 utils::ObjectCloneType clone_type)
131 {
132 obj.clip_start_pos_.set_ticks (other.clip_start_pos_.get_ticks ());
133 obj.loop_start_pos_.set_ticks (other.loop_start_pos_.get_ticks ());
134 obj.loop_end_pos_.set_ticks (other.loop_end_pos_.get_ticks ());
135 }
136
137 BOOST_DESCRIBE_CLASS (
138 ArrangerObjectLoopRange,
139 (),
140 (),
141 (),
142 (clip_start_pos_, loop_start_pos_, loop_end_pos_))
143
144private:
153 bool track_bounds_{ true };
154 std::optional<QMetaObject::Connection> track_bounds_connection_;
155
156 const ArrangerObjectBounds &bounds_;
157
164 dsp::AtomicPosition clip_start_pos_;
165 utils::QObjectUniquePtr<dsp::AtomicPositionQmlAdapter> clip_start_pos_adapter_;
166
168 dsp::AtomicPosition loop_start_pos_;
169 utils::QObjectUniquePtr<dsp::AtomicPositionQmlAdapter> loop_start_pos_adapter_;
170
176 dsp::AtomicPosition loop_end_pos_;
177 utils::QObjectUniquePtr<dsp::AtomicPositionQmlAdapter> loop_end_pos_adapter_;
178};
179
180} // namespace zrythm::structure::arrangement
units::sample_t get_samples() const
Helper method to get the position as samples.
Adds length functionality to arranger objects.
units::precise_tick_t get_loop_length_in_ticks() const
Returns the length of the loop in ticks.
units::sample_t get_loop_length_in_frames() const
Returns the length of the loop in frames.
int get_num_loops(bool count_incomplete) const
Returns the number of loops in the ArrangerObject, optionally including incomplete ones.