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 <string_view>
7
8#include "structure/arrangement/bounded_object.h"
9
10using std::literals::string_view_literals::operator""sv;
11
12namespace zrythm::structure::arrangement
13{
14
15class ArrangerObjectLoopRange : public QObject
16{
17 Q_OBJECT
18 Q_PROPERTY (
19 zrythm::dsp::AtomicPositionQmlAdapter * clipStartPosition READ
20 clipStartPosition CONSTANT)
21 Q_PROPERTY (
22 zrythm::dsp::AtomicPositionQmlAdapter * loopStartPosition READ
23 loopStartPosition CONSTANT)
24 Q_PROPERTY (
25 zrythm::dsp::AtomicPositionQmlAdapter * loopEndPosition READ loopEndPosition
26 CONSTANT)
27 Q_PROPERTY (
28 bool trackBounds READ trackBounds WRITE setTrackBounds NOTIFY
29 trackBoundsChanged)
30 Q_PROPERTY (bool looped READ looped NOTIFY loopedChanged)
31 QML_ELEMENT
32 QML_UNCREATABLE ("")
33
34public:
35 ArrangerObjectLoopRange (
36 const ArrangerObjectBounds &bounds,
37 QObject * parent = nullptr);
38
43 int get_num_loops (bool count_incomplete) const;
44
45 // ========================================================================
46 // QML Interface
47 // ========================================================================
48
49 dsp::AtomicPositionQmlAdapter * clipStartPosition () const
50 {
51 return clip_start_pos_adapter_.get ();
52 }
53 dsp::AtomicPositionQmlAdapter * loopStartPosition () const
54 {
55 return loop_start_pos_adapter_.get ();
56 }
57 dsp::AtomicPositionQmlAdapter * loopEndPosition () const
58 {
59 return loop_end_pos_adapter_.get ();
60 }
61 bool trackBounds () const { return track_bounds_; }
62 void setTrackBounds (bool track)
63 {
64 if (track_bounds_ != track)
65 {
66 track_bounds_ = track;
67 Q_EMIT trackBoundsChanged (track);
68 }
69 }
70 Q_SIGNAL void trackBoundsChanged (bool track);
71
72 bool looped () const
73 {
74 return loopStartPosition ()->samples () > 0
75 || clipStartPosition ()->samples () > 0
76 || length ()->samples () != loopEndPosition ()->samples ();
77 }
78 Q_SIGNAL void loopedChanged ();
79
80 Q_SIGNAL void loopableObjectPropertiesChanged ();
81
82 // ========================================================================
83
86
87 units::precise_tick_t get_loop_length_in_ticks () const
88 {
89 return std::max (
90 units::ticks (0.0),
91 loop_end_pos_.get_ticks () - loop_start_pos_.get_ticks ());
92 }
93
96
97 units::sample_t get_loop_length_in_frames () const
98 {
99 return std::max (
100 units::sample_t (units::samples (0)),
101 loop_end_pos_.get_samples () - loop_start_pos_.get_samples ());
102 }
103
104private:
105 auto length () const -> const dsp::AtomicPositionQmlAdapter *
106 {
107 return bounds_.length ();
108 }
109
110 static constexpr auto kClipStartPosKey = "clipStartPosition"sv;
111 static constexpr auto kLoopStartPosKey = "loopStartPosition"sv;
112 static constexpr auto kLoopEndPosKey = "loopEndPosition"sv;
113 static constexpr auto kTrackBoundsKey = "trackBounds"sv;
114 friend void
115 to_json (nlohmann::json &j, const ArrangerObjectLoopRange &object);
116 friend void
117 from_json (const nlohmann::json &j, ArrangerObjectLoopRange &object);
118
119 friend void init_from (
120 ArrangerObjectLoopRange &obj,
121 const ArrangerObjectLoopRange &other,
122 utils::ObjectCloneType clone_type)
123 {
124 obj.clip_start_pos_.set_ticks (other.clip_start_pos_.get_ticks ());
125 obj.loop_start_pos_.set_ticks (other.loop_start_pos_.get_ticks ());
126 obj.loop_end_pos_.set_ticks (other.loop_end_pos_.get_ticks ());
127 }
128
129 BOOST_DESCRIBE_CLASS (
130 ArrangerObjectLoopRange,
131 (),
132 (),
133 (),
134 (clip_start_pos_, loop_start_pos_, loop_end_pos_))
135
136private:
145 bool track_bounds_{ true };
146 std::optional<QMetaObject::Connection> track_bounds_connection_;
147
148 const ArrangerObjectBounds &bounds_;
149
156 dsp::AtomicPosition clip_start_pos_;
157 utils::QObjectUniquePtr<dsp::AtomicPositionQmlAdapter> clip_start_pos_adapter_;
158
160 dsp::AtomicPosition loop_start_pos_;
161 utils::QObjectUniquePtr<dsp::AtomicPositionQmlAdapter> loop_start_pos_adapter_;
162
168 dsp::AtomicPosition loop_end_pos_;
169 utils::QObjectUniquePtr<dsp::AtomicPositionQmlAdapter> loop_end_pos_adapter_;
170};
171
172} // 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.