Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
arranger_object_factory.h
1// SPDX-FileCopyrightText: © 2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "structure/arrangement/arranger_object_all.h"
7
8namespace zrythm::structure::arrangement
9{
13class ArrangerObjectFactory
14{
15public:
16 using SampleRateProvider = std::function<sample_rate_t ()>;
17 using BpmProvider = std::function<bpm_t ()>;
18
20 {
21 using MusicalModeGetter = std::function<bool ()>;
22 using LastTimelineObjectLengthProvider = std::function<double ()>;
23 using LastEditorObjectLengthProvider = std::function<double ()>;
24 using AutomationCurveAlgorithmProvider =
25 std::function<dsp::CurveOptions::Algorithm ()>;
26
27 const dsp::TempoMap &tempo_map_;
28 ArrangerObjectRegistry &object_registry_;
29 dsp::FileAudioSourceRegistry &file_audio_source_registry_;
30 MusicalModeGetter musical_mode_getter_;
31 LastTimelineObjectLengthProvider last_timeline_obj_len_provider_;
32 LastEditorObjectLengthProvider last_editor_obj_len_provider_;
33 AutomationCurveAlgorithmProvider automation_curve_algorithm_provider_;
34 };
35
36 ArrangerObjectFactory (
37 Dependencies dependencies,
38 SampleRateProvider sample_rate_provider,
39 BpmProvider bpm_provider)
40 : dependencies_ (std::move (dependencies)),
41 sample_rate_provider_ (std::move (sample_rate_provider)),
42 bpm_provider_ (std::move (bpm_provider))
43 {
44 }
45
46 template <structure::arrangement::FinalArrangerObjectSubclass ObjT>
47 class Builder
48 {
49 friend class ArrangerObjectFactory;
50
51 private:
52 explicit Builder (Dependencies dependencies)
53 : dependencies_ (std::move (dependencies))
54 {
55 }
56
57 Builder &with_clip (dsp::FileAudioSourceUuidReference clip_id)
58 requires (std::is_same_v<ObjT, AudioRegion>)
59 {
60 clip_id_.emplace (std::move (clip_id));
61 return *this;
62 }
63
64 public:
65 Builder &with_start_ticks (double start_ticks)
66 {
67 start_ticks_ = start_ticks;
68
69 return *this;
70 }
71
72 Builder &with_end_ticks (double end_ticks)
73 requires (BoundedObject<ObjT>)
74 {
75 end_ticks_ = end_ticks;
76 return *this;
77 }
78
79 Builder &with_name (const QString &name)
80 requires (NamedObject<ObjT>)
81 {
82 name_ = name;
83 return *this;
84 }
85
86 Builder &with_pitch (const int pitch)
87 requires (std::is_same_v<ObjT, MidiNote>)
88 {
89 pitch_ = pitch;
90 return *this;
91 }
92
93 Builder &with_velocity (const int vel)
94 requires (std::is_same_v<ObjT, MidiNote>)
95 {
96 velocity_ = vel;
97 return *this;
98 }
99
100 Builder &with_automatable_value (const double automatable_val)
101 requires (std::is_same_v<ObjT, AutomationPoint>)
102 {
103 automatable_value_ = automatable_val;
104 return *this;
105 }
106
107 Builder &with_chord_descriptor (const int chord_descriptor_index)
108 requires (std::is_same_v<ObjT, ChordObject>)
109 {
110 chord_descriptor_index_ = chord_descriptor_index;
111 return *this;
112 }
113
114 Builder &with_scale (utils::QObjectUniquePtr<dsp::MusicalScale> scale)
115 requires (std::is_same_v<ObjT, ScaleObject>)
116 {
117 scale_ = std::move (scale);
118 return *this;
119 }
120
121 Builder &with_marker_type (Marker::MarkerType marker_type)
122 requires (std::is_same_v<ObjT, Marker>)
123 {
124 marker_type_ = marker_type;
125 return *this;
126 }
127
131 std::unique_ptr<ObjT> build_empty () const
132 {
133 std::unique_ptr<ObjT> ret;
134 if constexpr (std::is_same_v<ObjT, AudioRegion>)
135 {
136 ret = std::make_unique<ObjT> (
137 dependencies_.tempo_map_, dependencies_.object_registry_,
138 dependencies_.file_audio_source_registry_,
139 dependencies_.musical_mode_getter_);
140 }
141 else if constexpr (RegionObject<ObjT>)
142 {
143 ret = std::make_unique<ObjT> (
144 dependencies_.tempo_map_, dependencies_.object_registry_,
145 dependencies_.file_audio_source_registry_);
146 }
147 else if constexpr (std::is_same_v<ObjT, Marker>)
148 {
149 ret = std::make_unique<ObjT> (
150 dependencies_.tempo_map_, Marker::MarkerType::Custom);
151 }
152 else if constexpr (std::is_same_v<ObjT, AudioSourceObject>)
153 {
154 ret = std::make_unique<ObjT> (
155 dependencies_.tempo_map_, dependencies_.file_audio_source_registry_,
156 dsp::FileAudioSourceUuidReference{
157 dependencies_.file_audio_source_registry_ });
158 }
159 else
160 {
161 ret = std::make_unique<ObjT> (dependencies_.tempo_map_);
162 }
163 return ret;
164 }
165
166 auto build_in_registry ()
167 {
168 auto obj_ref = [&] () {
169 auto obj_unique_ptr = build_empty ();
170 dependencies_.object_registry_.register_object (obj_unique_ptr.get ());
171 structure::arrangement::ArrangerObjectUuidReference ret_ref{
172 obj_unique_ptr->get_uuid (), dependencies_.object_registry_
173 };
174 obj_unique_ptr.release ();
175 return ret_ref;
176 }();
177
178 auto * obj = std::get<ObjT *> (obj_ref.get_object ());
179
180 if constexpr (RegionObject<ObjT>)
181 {
182 obj->loopRange ()->setTrackBounds (true);
183 }
184
185 if (start_ticks_)
186 {
187 if (!end_ticks_ && !clip_id_)
188 {
189 if constexpr (BoundedObject<ObjT>)
190 {
191 double len_ticks{};
192 if constexpr (TimelineObject<ObjT>)
193 {
194 len_ticks =
195 dependencies_.last_timeline_obj_len_provider_ ();
196 }
197 else
198 {
199 len_ticks = dependencies_.last_editor_obj_len_provider_ ();
200 }
201 get_object_bounds (*obj)->length ()->setTicks (len_ticks);
202 }
203 }
204 obj->position ()->setTicks (*start_ticks_);
205 }
206
207 if (clip_id_)
208 {
209 if constexpr (std::is_same_v<ObjT, AudioRegion>)
210 {
211 auto source_object = dependencies_.object_registry_.create_object<
212 AudioSourceObject> (
213 dependencies_.tempo_map_,
214 dependencies_.file_audio_source_registry_, clip_id_.value ());
215 obj->set_source (source_object);
216 obj->bounds ()->length ()->setSamples (
217 clip_id_.value ()
218 .template get_object_as<dsp::FileAudioSource> ()
219 ->get_num_frames ());
220 }
221 }
222
223 if (end_ticks_)
224 {
225 if constexpr (BoundedObject<ObjT>)
226 {
227 get_object_bounds (*obj)->length ()->setTicks (
228 *end_ticks_ - obj->position ()->ticks ());
229 }
230 }
231
232 if (name_)
233 {
234 if constexpr (NamedObject<ObjT>)
235 {
236 if constexpr (RegionObject<ObjT>)
237 {
238 obj->name ()->setName (*name_);
239 }
240 else
241 {
242 obj->name ()->setName (*name_);
243 }
244 }
245 }
246
247 if (pitch_)
248 {
249 if constexpr (std::is_same_v<ObjT, MidiNote>)
250 {
251 obj->setPitch (*pitch_);
252 }
253 }
254
255 if (velocity_)
256 {
257 if constexpr (std::is_same_v<ObjT, MidiNote>)
258 {
259 obj->setVelocity (*velocity_);
260 }
261 }
262
263 if (automatable_value_)
264 {
265 if constexpr (std::is_same_v<ObjT, AutomationPoint>)
266 {
267 obj->setValue (static_cast<float> (*automatable_value_));
268 }
269 }
270
271 if (scale_)
272 {
273 if constexpr (std::is_same_v<ObjT, ScaleObject>)
274 {
275 obj->setScale (scale_.release ());
276 }
277 }
278
279 if constexpr (std::is_same_v<ObjT, AutomationPoint>)
280 {
281 obj->curveOpts ()->setAlgorithm (
282 dependencies_.automation_curve_algorithm_provider_ ());
283 }
284
285 if (chord_descriptor_index_)
286 {
287 if constexpr (std::is_same_v<ObjT, ChordObject>)
288 {
289 obj->setChordDescriptorIndex (*chord_descriptor_index_);
290 }
291 }
292
293 return obj_ref;
294 }
295
296 private:
297 Dependencies dependencies_;
298 std::optional<dsp::FileAudioSourceUuidReference> clip_id_;
299 std::optional<double> start_ticks_;
300 std::optional<double> end_ticks_;
301 std::optional<QString> name_;
302 std::optional<int> pitch_;
303 std::optional<double> automatable_value_;
304 std::optional<int> chord_descriptor_index_;
305 utils::QObjectUniquePtr<dsp::MusicalScale> scale_;
306 std::optional<int> velocity_;
307 std::optional<Marker::MarkerType> marker_type_;
308 };
309
310 template <typename ObjT> auto get_builder () const
311 {
312 return Builder<ObjT> (dependencies_);
313 }
314
315public:
320 dsp::FileAudioSourceUuidReference clip_id,
321 double startTicks) const
322 {
323 auto obj =
324 get_builder<structure::arrangement::AudioRegion> ()
325 .with_start_ticks (startTicks)
326 .with_clip (std::move (clip_id))
327 .build_in_registry ();
328 return obj;
329 }
330
331 auto create_empty_audio_region_for_recording (
332 int num_channels,
333 const utils::Utf8String &clip_name,
334 double start_ticks)
335 {
336 auto clip = dependencies_.file_audio_source_registry_.create_object<
338 num_channels, 1, sample_rate_provider_ (), bpm_provider_ (), clip_name);
339 return create_audio_region_with_clip (std::move (clip), start_ticks);
340 }
341
342 auto create_audio_region_from_file (const QString &absPath, double startTicks)
343 {
344 auto clip = dependencies_.file_audio_source_registry_.create_object<
346 utils::Utf8String::from_qstring (absPath), sample_rate_provider_ (),
347 bpm_provider_ ());
348 return create_audio_region_with_clip (std::move (clip), startTicks);
349 }
350
358 const utils::audio::AudioBuffer &buf,
359 utils::audio::BitDepth bit_depth,
360 const utils::Utf8String &clip_name,
361 double start_ticks) const
362 {
363 auto clip = dependencies_.file_audio_source_registry_.create_object<
365 buf, bit_depth, sample_rate_provider_ (), bpm_provider_ (), clip_name);
366 return create_audio_region_with_clip (std::move (clip), start_ticks);
367 }
368
376 template <structure::arrangement::RegionObject RegionT>
377 auto create_editor_object (double startTicks, std::variant<int, double> value)
378 requires (!std::is_same_v<RegionT, structure::arrangement::AudioRegion>)
379 {
380 using ChildT = typename RegionT::ArrangerObjectChildType;
381 auto builder =
382 std::move (get_builder<ChildT> ().with_start_ticks (startTicks));
383 if constexpr (std::is_same_v<ChildT, MidiNote>)
384 {
385 const auto ival = std::get<int> (value);
386 assert (ival >= 0 && ival < 128);
387 builder.with_pitch (ival);
388 }
389 else if constexpr (std::is_same_v<ChildT, AutomationPoint>)
390 {
391 builder.with_automatable_value (std::get<double> (value));
392 }
393 else if constexpr (std::is_same_v<ChildT, ChordObject>)
394 {
395 builder.with_chord_descriptor (std::get<int> (value));
396 }
397 return builder.build_in_registry ();
398 }
399
400 template <structure::arrangement::FinalArrangerObjectSubclass ObjT>
401 auto clone_new_object_identity (const ObjT &other) const
402 {
403 if constexpr (std::is_same_v<ObjT, AudioRegion>)
404 {
405 return dependencies_.object_registry_.clone_object (
406 other, dependencies_.tempo_map_, dependencies_.object_registry_,
407 dependencies_.file_audio_source_registry_,
408 dependencies_.musical_mode_getter_);
409 }
410 else if constexpr (RegionObject<ObjT>)
411 {
412 return dependencies_.object_registry_.clone_object (
413 other, dependencies_.tempo_map_, dependencies_.object_registry_,
414 dependencies_.file_audio_source_registry_);
415 }
416 else if constexpr (std::is_same_v<ObjT, Marker>)
417 {
418 return dependencies_.object_registry_.clone_object (
419 other, dependencies_.tempo_map_, other.markerType ());
420 }
421 else
422 {
423 return dependencies_.object_registry_.clone_object (
424 other, dependencies_.tempo_map_);
425 }
426 }
427
428// deprecated - no use case for snapshots, just serialize straight to/from json
429#if 0
430 template <structure::arrangement::FinalArrangerObjectSubclass ObjT>
431 auto clone_object_snapshot (const ObjT &other, QObject &owner) const
432 {
433 ObjT * new_obj{};
434 if constexpr (std::is_same_v<ObjT, AudioRegion>)
435 {
436 // TODO
437 new_obj = utils::clone_qobject (
438 other, &owner, utils::ObjectCloneType::Snapshot, object_registry_,
439 file_audio_source_registry_);
440 }
441 else if constexpr (std::is_same_v<ObjT, Marker>)
442 {
443 new_obj = utils::clone_qobject (
444 other, &owner, utils::ObjectCloneType::Snapshot, object_registry_,
445 [] (const auto &name) { return true; });
446 }
447 else
448 {
449 new_obj = utils::clone_qobject (
450 other, &owner, utils::ObjectCloneType::Snapshot, object_registry_);
451 }
452 return new_obj;
453 }
454#endif
455
456private:
457 Dependencies dependencies_;
458 SampleRateProvider sample_rate_provider_;
459 BpmProvider bpm_provider_;
460};
461}
Algorithm
The algorithm to use for curves.
Definition curve.h:32
Audio clips for the pool.
std::unique_ptr< ObjT > build_empty() const
Returns an instantiated object to be used for deserialization.
auto create_editor_object(double startTicks, std::variant< int, double > value)
Used to create and add editor objects.
auto create_audio_region_with_clip(dsp::FileAudioSourceUuidReference clip_id, double startTicks) const
To be used by the backend.
auto create_audio_region_from_audio_buffer(const utils::audio::AudioBuffer &buf, utils::audio::BitDepth bit_depth, const utils::Utf8String &clip_name, double start_ticks) const
Creates and registers a new AudioClip and then creates and returns an AudioRegion from it.
A unique pointer for QObject objects that also works with QObject-based ownership.
Definition qt.h:38
Lightweight UTF-8 string wrapper with safe conversions.
Definition utf8_string.h:38
uint32_t sample_rate_t
Sample rate.
Definition types.h:61
float bpm_t
The BPM type.
Definition types.h:73
@ Snapshot
Creates a snapshot of the object with the same identity.
Definition icloneable.h:23