Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
arranger_object_owner.h
1// SPDX-FileCopyrightText: © 2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "dsp/file_audio_source.h"
7#include "structure/arrangement/arranger_object.h"
8#include "structure/arrangement/arranger_object_list_model.h"
9
10#define DEFINE_ARRANGER_OBJECT_OWNER_QML_PROPERTIES( \
11 ClassType, QPropertyName, ChildType) \
12public: \
13 Q_PROPERTY ( \
14 zrythm::structure::arrangement::ArrangerObjectListModel * QPropertyName \
15 READ QPropertyName CONSTANT) \
16 structure::arrangement::ArrangerObjectListModel * QPropertyName () const \
17 { \
18 return ArrangerObjectOwner<ChildType>::get_model (); \
19 }
20
21namespace zrythm::structure::arrangement
22{
23template <FinalArrangerObjectSubclass ChildT> class ArrangerObjectOwner
24{
25public:
26 using ArrangerObjectChildType = ChildT;
27 using ArrangerObjectListModel =
29
30 template <typename T>
31 explicit ArrangerObjectOwner (
32 ArrangerObjectRegistry &registry,
33 dsp::FileAudioSourceRegistry &file_audio_source_registry,
34 T &derived)
35 requires std::derived_from<T, QObject>
36 : registry_ (registry),
37 file_audio_source_registry_ (file_audio_source_registry)
38 {
39 if constexpr (std::derived_from<T, ArrangerObject>)
40 {
41 list_model_ = utils::make_qobject_unique<ArrangerObjectListModel> (
42 children_, derived);
43 }
44 else
45 {
46 list_model_ = utils::make_qobject_unique<ArrangerObjectListModel> (
47 children_, &derived);
48 }
49 }
50 virtual ~ArrangerObjectOwner () = default;
51 Z_DISABLE_COPY_MOVE (ArrangerObjectOwner)
52
53 auto &get_children_vector () const
54 {
55 return children_.get<random_access_index> ();
56 }
57
58 auto get_children_view () const
59 {
60 const auto &vec_ref = children_.get<random_access_index> ();
61 return vec_ref
62 | std::views::transform (
63 &ArrangerObjectUuidReference::get_object_as<ChildT>);
64 }
65
66 auto get_sorted_children_view () const
67 {
68 const auto &vec_ref = children_.get<sorted_index> ();
69 return vec_ref
70 | std::views::transform (
71 &ArrangerObjectUuidReference::get_object_as<ChildT>);
72 }
73
74 void add_ticks_to_children (double ticks)
75 {
76 for (auto * child : get_children_view ())
77 {
78 child->position ()->setTicks (child->position ()->ticks () + ticks);
79 }
80 }
81
86 {
87 return children_.get<uuid_hash_index> ().contains (id);
88 }
89
90 ArrangerObjectListModel * get_model () const { return list_model_.get (); }
91
92 template <typename SelfT>
93 ArrangerObjectUuidReference
94 remove_object (this SelfT &self, const ArrangerObject::Uuid &id)
95 {
96 auto &container = self.ArrangerObjectOwner<ChildT>::children_;
97 auto &random_access_idx = container.template get<random_access_index> ();
98 auto it_to_remove = std::ranges::find (
99 random_access_idx, id, &ArrangerObjectUuidReference::id);
100 if (it_to_remove == random_access_idx.end ())
101 {
102 throw std::runtime_error (
103 fmt::format ("object to remove not found: {}", id));
104 }
105 z_trace ("removing object: {}", id);
106
107 auto obj_ref = *it_to_remove;
108 const auto remove_idx =
109 std::distance (random_access_idx.begin (), it_to_remove);
110
111 // Remove from model first to handle Qt signals properly
112 self.ArrangerObjectOwner<ChildT>::list_model_->removeRows (
113 static_cast<int> (remove_idx), 1);
114
115 return obj_ref;
116 }
117
118 template <typename SelfT>
119 void insert_object (
120 this SelfT &self,
121 const ArrangerObjectUuidReference &obj_ref,
122 int idx)
123 {
124 self.ArrangerObjectOwner<ChildT>::list_model_->insertObject (obj_ref, idx);
125 }
126
127 template <typename SelfT>
128 void add_object (this SelfT &self, const ArrangerObjectUuidReference &obj_ref)
129 {
130 self.ArrangerObjectOwner<ChildT>::insert_object (
131 obj_ref,
132 static_cast<int> (self.ArrangerObjectOwner<ChildT>::children_.size ()));
133 }
134
135 void clear_objects () { list_model_->clear (); }
136
146 virtual std::string
147 get_field_name_for_serialization (const ChildT *) const = 0;
148
149 friend void init_from (
150 ArrangerObjectOwner &obj,
151 const ArrangerObjectOwner &other,
152 utils::ObjectCloneType clone_type)
153 {
154 if (clone_type == utils::ObjectCloneType::NewIdentity)
155 {
156 obj.children_.reserve (other.children_.size ());
157 for (const auto &child : other.get_children_view ())
158 {
159 // FIXME: not ideal to have child-specific logic here...
160 std::optional<ArrangerObjectUuidReference> clone_ref;
161 if constexpr (std::is_same_v<ChildT, AudioSourceObject>)
162 {
163 clone_ref = obj.registry_.clone_object (
164 *child, child->get_tempo_map (),
165 obj.file_audio_source_registry_, child->audio_source_ref ());
166 obj.children_.get<sequenced_index> ().emplace_back (
167 std::move (*clone_ref));
168 }
169 else if constexpr (RegionObject<ChildT>)
170 {
171 // TODO
172 }
173 else if constexpr (std::is_same_v<ChildT, Marker>)
174 {
175 clone_ref = obj.registry_.clone_object (
176 *child, child->get_tempo_map (), child->markerType ());
177 obj.children_.get<sequenced_index> ().emplace_back (
178 std::move (*clone_ref));
179 }
180 else
181 {
182 clone_ref =
183 obj.registry_.clone_object (*child, child->get_tempo_map ());
184 obj.children_.get<sequenced_index> ().emplace_back (
185 std::move (*clone_ref));
186 }
187 }
188 }
189 }
190
191private:
192 friend void to_json (nlohmann::json &j, const ArrangerObjectOwner &obj)
193 {
194 const auto children_field_name =
195 obj.get_field_name_for_serialization (static_cast<ChildT *> (nullptr));
196 j[children_field_name] = obj.children_;
197 }
198 friend void from_json (const nlohmann::json &j, ArrangerObjectOwner &obj)
199 {
200 const auto children_field_name =
201 obj.get_field_name_for_serialization (static_cast<ChildT *> (nullptr));
202 for (const auto &child_json : j.at (children_field_name))
203 {
204 const auto uuid =
205 child_json.at (ArrangerObjectUuidReference::kIdKey)
206 .template get<ArrangerObjectUuid> ();
207 ArrangerObjectUuidReference obj_ref{ uuid, obj.registry_ };
208 obj.children_.get<sequenced_index> ().emplace_back (std::move (obj_ref));
209 }
210 }
211
212private:
213 ArrangerObjectRegistry &registry_;
214 dsp::FileAudioSourceRegistry &file_audio_source_registry_;
215 ArrangerObjectRefMultiIndexContainer children_;
216 utils::QObjectUniquePtr<ArrangerObjectListModel> list_model_;
217
218 BOOST_DESCRIBE_CLASS (ArrangerObjectOwner<ChildT>, (), (), (), (children_))
219};
220} // namespace zrythm::structure::arrangement
virtual std::string get_field_name_for_serialization(const ChildT *) const =0
Get the children field name to be used during serialization/deserialization.
bool contains_object(const ArrangerObject::Uuid &id) const
O(1) lookup if an object with the given ID exists.
@ NewIdentity
Creates a separately identified object.
Definition icloneable.h:30