Zrythm v2.0.0-alpha.1
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
clip.h
1// SPDX-FileCopyrightText: © 2026 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <optional>
7#include <string_view>
8
9#include "dsp/content_time_warp.h"
10#include "dsp/position.h"
11#include "dsp/tempo_map_qml_adapter.h"
12#include "dsp/timebase.h"
13#include "structure/arrangement/arranger_object.h"
14#include "utils/icloneable.h"
15
16#include <boost/describe/class.hpp>
17#include <nlohmann/json_fwd.hpp>
18
19using std::literals::string_view_literals::operator""sv;
20
21namespace zrythm::structure::arrangement
22{
23
36class Clip : public ArrangerObject
37{
38 Q_OBJECT
39 Q_PROPERTY (
40 zrythm::dsp::ContentTimeWarp * contentWarp READ contentWarp CONSTANT)
41 Q_PROPERTY (
42 zrythm::dsp::timebase::TimebaseProvider * timebaseProvider READ
43 timebaseProvider CONSTANT)
44 Q_PROPERTY (
45 double timelineLengthTicks READ timelineLengthTicks NOTIFY
46 timelineLengthTicksChanged)
47
48 // Loop range properties (merged from ArrangerObjectLoopRange)
49 Q_PROPERTY (
50 zrythm::dsp::ContentPosition * clipStartPosition READ clipStartPosition
51 CONSTANT)
52 Q_PROPERTY (
53 zrythm::dsp::ContentPosition * loopStartPosition READ loopStartPosition
54 CONSTANT)
55 Q_PROPERTY (
56 zrythm::dsp::ContentPosition * loopEndPosition READ loopEndPosition CONSTANT)
57 Q_PROPERTY (
58 bool trackBounds READ trackBounds WRITE setTrackBounds NOTIFY
59 trackBoundsChanged)
60 Q_PROPERTY (bool looped READ looped NOTIFY loopedChanged)
61 QML_ELEMENT
62 QML_UNCREATABLE ("")
63
64public:
65 ~Clip () noexcept override;
66 Q_DISABLE_COPY_MOVE (Clip)
67
68 dsp::TimelinePosition * position () const override
69 {
70 return qobject_cast<dsp::TimelinePosition *> (ArrangerObject::position ());
71 }
72
73 // ========================================================================
74 // QML Interface
75 // ========================================================================
76
77 dsp::ContentTimeWarp * contentWarp () const { return content_warp_.get (); }
78
79 dsp::TimebaseProvider * timebaseProvider () const
80 {
81 return timebase_provider_.get ();
82 }
83
84 // Convenience method
85 Q_INVOKABLE void setLengthTicks (double ticks)
86 {
87 length ()->setTicks (ticks);
88 }
89
99 Q_INVOKABLE QList<double>
100 loopPointTimelineTicks (double displayEndTicks) const;
101
109 double timelineLengthTicks () const;
110 Q_SIGNAL void timelineLengthTicksChanged ();
111
118 units::sample_t get_end_position_samples (bool end_position_inclusive) const;
119
124 units::sample_t get_sample_duration () const;
125
129 [[nodiscard]] bool
130 is_hit (const units::sample_t frames, bool object_end_pos_inclusive = false)
131 const;
132
136 bool is_hit_by_range (
137 std::pair<units::sample_t, units::sample_t> global_frames,
138 bool range_start_inclusive = true,
139 bool range_end_inclusive = true,
140 bool object_end_pos_inclusive = false) const;
141
142 // ========================================================================
143 // Loop Range (merged from ArrangerObjectLoopRange)
144 // ========================================================================
145
146 dsp::ContentPosition * clipStartPosition () const
147 {
148 return clip_start_pos_.get ();
149 }
150 dsp::ContentPosition * loopStartPosition () const
151 {
152 return loop_start_pos_.get ();
153 }
154 dsp::ContentPosition * loopEndPosition () const
155 {
156 return loop_end_pos_.get ();
157 }
158
159 bool trackBounds () const { return track_bounds_; }
160 void setTrackBounds (bool track);
161 Q_SIGNAL void trackBoundsChanged (bool track);
162
183 void set_loop_range (
184 dsp::ContentTick clip_start,
185 dsp::ContentTick loop_start,
186 dsp::ContentTick loop_end);
187
188 bool looped () const;
189 Q_SIGNAL void loopedChanged ();
190
199 Q_SIGNAL void contentChanged ();
200
208
212 int get_num_loops (bool count_incomplete) const;
213
217 dsp::ContentTick get_loop_length_in_ticks () const
218 {
219 return max (
220 dsp::ContentTick (units::ticks (0.0)),
221 loop_end_pos_->asTick () - loop_start_pos_->asTick ());
222 }
223
227 virtual void shift_all_children (dsp::ContentTick delta) = 0;
228
234 virtual std::optional<dsp::ContentTick> first_child_position () const = 0;
235
236 // ========================================================================
237
238protected:
239 Clip (
240 Type type,
241 const dsp::TempoMapWrapper &tempo_map_wrapper,
242 QObject * parent = nullptr) noexcept;
243
244 friend void
245 init_from (Clip &obj, const Clip &other, utils::ObjectCloneType clone_type);
246 friend void to_json (nlohmann::json &j, const Clip &clip);
247 friend void from_json (const nlohmann::json &j, Clip &clip);
248
249private:
250 static constexpr auto kTimebaseKey = "timebase"sv;
251 static constexpr auto kClipStartPosKey = "clipStartPosition"sv;
252 static constexpr auto kLoopStartPosKey = "loopStartPosition"sv;
253 static constexpr auto kLoopEndPosKey = "loopEndPosition"sv;
254 static constexpr auto kTrackBoundsKey = "trackBounds"sv;
255
256private:
257 utils::QObjectUniquePtr<dsp::ContentTimeWarp> content_warp_;
258 utils::QObjectUniquePtr<dsp::TimebaseProvider> timebase_provider_;
259
260 // Loop range members (merged from ArrangerObjectLoopRange)
261
263 utils::QObjectUniquePtr<dsp::ContentPosition> clip_start_pos_;
264
266 utils::QObjectUniquePtr<dsp::ContentPosition> loop_start_pos_;
267
269 utils::QObjectUniquePtr<dsp::ContentPosition> loop_end_pos_;
270
279 bool track_bounds_{ true };
280 std::optional<QMetaObject::Connection> track_bounds_connection_;
281
282 BOOST_DESCRIBE_CLASS (Clip, (ArrangerObject), (), (), ())
283};
284
285} // namespace zrythm::structure::arrangement
A Position whose ticks are content-space (clip-local) ticks.
Definition position.h:118
Maintains the mapping between clip-native content ticks and project timeline ticks.
A Position whose ticks are absolute timeline ticks.
Definition position.h:97
Self-nesting timebase provider that resolves an effective timebase through a chain of providers.
Definition timebase.h:60
Base class for all objects in the arranger.
ArrangerObject(Type type, const dsp::TempoMapWrapper &tempo_map_wrapper, ArrangerObjectFeatures features, QObject *parent=nullptr) noexcept
Construct a new ArrangerObject.
Intermediate base class for all clip types.
Definition clip.h:37
int get_num_loops(bool count_incomplete) const
Returns the number of loops, optionally including incomplete ones.
Q_INVOKABLE QList< double > loopPointTimelineTicks(double displayEndTicks) const
Timeline delta-tick positions of each loop wrap point.
units::sample_t get_end_position_samples(bool end_position_inclusive) const
Returns the end position of this clip in timeline samples.
dsp::ContentTick get_loop_length_in_ticks() const
Returns the length of the loop in ticks.
Definition clip.h:207
virtual std::optional< dsp::ContentTick > first_child_position() const =0
Returns the position of the first child, if any.
Q_SIGNAL void contentChanged()
Emitted when the clip's content changes.
bool is_hit(const units::sample_t frames, bool object_end_pos_inclusive=false) const
Returns whether the clip is hit by the given timeline position.
void set_loop_range(dsp::ContentTick clip_start, dsp::ContentTick loop_start, dsp::ContentTick loop_end)
Set the three loop positions atomically, bypassing the per-position clamping constraints.
bool is_hit_by_range(std::pair< units::sample_t, units::sample_t > global_frames, bool range_start_inclusive=true, bool range_end_inclusive=true, bool object_end_pos_inclusive=false) const
Whether the clip is hit by the given range.
units::sample_t get_sample_duration() const
Returns the duration of this clip in timeline samples (end minus start), warp-aware.
double timelineLengthTicks() const
Warp-aware length of this clip in timeline ticks.
virtual void shift_all_children(dsp::ContentTick delta)=0
Shifts all child objects by delta content ticks.
Q_SIGNAL void loopablePropertiesChanged()
Emitted when any loop-related property changes.
String utilities.