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<units::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_,
151 marker_type_.value_or (Marker::MarkerType::Custom));
152 }
153 else if constexpr (std::is_same_v<ObjT, AudioSourceObject>)
154 {
155 auto file_audio_source =
156 dependencies_.file_audio_source_registry_.create_object<
158 1, 1, units::sample_rate (44100), 120, u8"dummy");
159 ret = std::make_unique<ObjT> (
160 dependencies_.tempo_map_, dependencies_.file_audio_source_registry_,
161 file_audio_source);
162 }
163 else
164 {
165 ret = std::make_unique<ObjT> (dependencies_.tempo_map_);
166 }
167 return ret;
168 }
169
170 auto build_in_registry ()
171 {
172 auto obj_ref = [&] () {
173 auto obj_unique_ptr = build_empty ();
174 dependencies_.object_registry_.register_object (obj_unique_ptr.get ());
175 structure::arrangement::ArrangerObjectUuidReference ret_ref{
176 obj_unique_ptr->get_uuid (), dependencies_.object_registry_
177 };
178 obj_unique_ptr.release ();
179 return ret_ref;
180 }();
181
182 auto * obj = std::get<ObjT *> (obj_ref.get_object ());
183
184 if constexpr (RegionObject<ObjT>)
185 {
186 obj->loopRange ()->setTrackBounds (true);
187 }
188
189 if (start_ticks_)
190 {
191 if (!end_ticks_ && !clip_id_)
192 {
193 if constexpr (BoundedObject<ObjT>)
194 {
195 double len_ticks{};
196 if constexpr (TimelineObject<ObjT>)
197 {
198 len_ticks =
199 dependencies_.last_timeline_obj_len_provider_ ();
200 }
201 else
202 {
203 len_ticks = dependencies_.last_editor_obj_len_provider_ ();
204 }
205 get_object_bounds (*obj)->length ()->setTicks (len_ticks);
206 }
207 }
208 obj->position ()->setTicks (*start_ticks_);
209 }
210
211 if (clip_id_)
212 {
213 if constexpr (std::is_same_v<ObjT, AudioRegion>)
214 {
215 auto source_object = dependencies_.object_registry_.create_object<
216 AudioSourceObject> (
217 dependencies_.tempo_map_,
218 dependencies_.file_audio_source_registry_, clip_id_.value ());
219 obj->set_source (source_object);
220 obj->bounds ()->length ()->setSamples (
221 clip_id_.value ()
222 .template get_object_as<dsp::FileAudioSource> ()
223 ->get_num_frames ());
224 }
225 }
226
227 if (end_ticks_)
228 {
229 if constexpr (BoundedObject<ObjT>)
230 {
231 get_object_bounds (*obj)->length ()->setTicks (
232 *end_ticks_ - obj->position ()->ticks ());
233 }
234 }
235
236 if (name_)
237 {
238 if constexpr (NamedObject<ObjT>)
239 {
240 if constexpr (RegionObject<ObjT>)
241 {
242 obj->name ()->setName (*name_);
243 }
244 else
245 {
246 obj->name ()->setName (*name_);
247 }
248 }
249 }
250
251 if (pitch_)
252 {
253 if constexpr (std::is_same_v<ObjT, MidiNote>)
254 {
255 obj->setPitch (*pitch_);
256 }
257 }
258
259 if (velocity_)
260 {
261 if constexpr (std::is_same_v<ObjT, MidiNote>)
262 {
263 obj->setVelocity (*velocity_);
264 }
265 }
266
267 if (automatable_value_)
268 {
269 if constexpr (std::is_same_v<ObjT, AutomationPoint>)
270 {
271 obj->setValue (static_cast<float> (*automatable_value_));
272 }
273 }
274
275 if (scale_)
276 {
277 if constexpr (std::is_same_v<ObjT, ScaleObject>)
278 {
279 obj->setScale (scale_.release ());
280 }
281 }
282
283 if constexpr (std::is_same_v<ObjT, AutomationPoint>)
284 {
285 obj->curveOpts ()->setAlgorithm (
286 dependencies_.automation_curve_algorithm_provider_ ());
287 }
288
289 if (chord_descriptor_index_)
290 {
291 if constexpr (std::is_same_v<ObjT, ChordObject>)
292 {
293 obj->setChordDescriptorIndex (*chord_descriptor_index_);
294 }
295 }
296
297 return obj_ref;
298 }
299
300 private:
301 Dependencies dependencies_;
302 std::optional<dsp::FileAudioSourceUuidReference> clip_id_;
303 std::optional<double> start_ticks_;
304 std::optional<double> end_ticks_;
305 std::optional<QString> name_;
306 std::optional<int> pitch_;
307 std::optional<double> automatable_value_;
308 std::optional<int> chord_descriptor_index_;
309 utils::QObjectUniquePtr<dsp::MusicalScale> scale_;
310 std::optional<int> velocity_;
311 std::optional<Marker::MarkerType> marker_type_;
312 };
313
314 template <typename ObjT> auto get_builder () const
315 {
316 return Builder<ObjT> (dependencies_);
317 }
318
319public:
324 dsp::FileAudioSourceUuidReference clip_id,
325 double startTicks) const
326 {
327 auto obj =
328 get_builder<structure::arrangement::AudioRegion> ()
329 .with_start_ticks (startTicks)
330 .with_clip (std::move (clip_id))
331 .build_in_registry ();
332 return obj;
333 }
334
335 auto create_empty_audio_region_for_recording (
336 int num_channels,
337 const utils::Utf8String &clip_name,
338 double start_ticks)
339 {
340 auto clip = dependencies_.file_audio_source_registry_.create_object<
342 num_channels, 1, sample_rate_provider_ (), bpm_provider_ (), clip_name);
343 return create_audio_region_with_clip (std::move (clip), start_ticks);
344 }
345
346 auto create_audio_region_from_file (const QString &absPath, double startTicks)
347 {
348 auto clip = dependencies_.file_audio_source_registry_.create_object<
350 utils::Utf8String::from_qstring (absPath), sample_rate_provider_ (),
351 bpm_provider_ ());
352 return create_audio_region_with_clip (std::move (clip), startTicks);
353 }
354
362 const utils::audio::AudioBuffer &buf,
363 utils::audio::BitDepth bit_depth,
364 const utils::Utf8String &clip_name,
365 double start_ticks) const
366 {
367 auto clip = dependencies_.file_audio_source_registry_.create_object<
369 buf, bit_depth, sample_rate_provider_ (), bpm_provider_ (), clip_name);
370 return create_audio_region_with_clip (std::move (clip), start_ticks);
371 }
372
380 template <structure::arrangement::RegionObject RegionT>
381 auto create_editor_object (double startTicks, std::variant<int, double> value)
382 requires (!std::is_same_v<RegionT, structure::arrangement::AudioRegion>)
383 {
384 using ChildT = typename RegionT::ArrangerObjectChildType;
385 auto builder =
386 std::move (get_builder<ChildT> ().with_start_ticks (startTicks));
387 if constexpr (std::is_same_v<ChildT, MidiNote>)
388 {
389 const auto ival = std::get<int> (value);
390 assert (ival >= 0 && ival < 128);
391 builder.with_pitch (ival);
392 }
393 else if constexpr (std::is_same_v<ChildT, AutomationPoint>)
394 {
395 builder.with_automatable_value (std::get<double> (value));
396 }
397 else if constexpr (std::is_same_v<ChildT, ChordObject>)
398 {
399 builder.with_chord_descriptor (std::get<int> (value));
400 }
401 return builder.build_in_registry ();
402 }
403
404 template <structure::arrangement::FinalArrangerObjectSubclass ObjT>
405 auto clone_new_object_identity (const ObjT &other) const
406 {
407 if constexpr (std::is_same_v<ObjT, AudioRegion>)
408 {
409 return dependencies_.object_registry_.clone_object (
410 other, dependencies_.tempo_map_, dependencies_.object_registry_,
411 dependencies_.file_audio_source_registry_,
412 dependencies_.musical_mode_getter_);
413 }
414 else if constexpr (RegionObject<ObjT>)
415 {
416 return dependencies_.object_registry_.clone_object (
417 other, dependencies_.tempo_map_, dependencies_.object_registry_,
418 dependencies_.file_audio_source_registry_);
419 }
420 else if constexpr (std::is_same_v<ObjT, Marker>)
421 {
422 return dependencies_.object_registry_.clone_object (
423 other, dependencies_.tempo_map_, other.markerType ());
424 }
425 else if constexpr (std::is_same_v<ObjT, AudioSourceObject>)
426 {
427 return dependencies_.object_registry_.clone_object (
428 other, dependencies_.tempo_map_,
429 dependencies_.file_audio_source_registry_, other.audio_source_ref ());
430 }
431 else
432 {
433 return dependencies_.object_registry_.clone_object (
434 other, dependencies_.tempo_map_);
435 }
436 }
437
438// deprecated - no use case for snapshots, just serialize straight to/from json
439#if 0
440 template <structure::arrangement::FinalArrangerObjectSubclass ObjT>
441 auto clone_object_snapshot (const ObjT &other, QObject &owner) const
442 {
443 ObjT * new_obj{};
444 if constexpr (std::is_same_v<ObjT, AudioRegion>)
445 {
446 // TODO
447 new_obj = utils::clone_qobject (
448 other, &owner, utils::ObjectCloneType::Snapshot, object_registry_,
449 file_audio_source_registry_);
450 }
451 else if constexpr (std::is_same_v<ObjT, Marker>)
452 {
453 new_obj = utils::clone_qobject (
454 other, &owner, utils::ObjectCloneType::Snapshot, object_registry_,
455 [] (const auto &name) { return true; });
456 }
457 else
458 {
459 new_obj = utils::clone_qobject (
460 other, &owner, utils::ObjectCloneType::Snapshot, object_registry_);
461 }
462 return new_obj;
463 }
464#endif
465
466private:
467 Dependencies dependencies_;
468 SampleRateProvider sample_rate_provider_;
469 BpmProvider bpm_provider_;
470};
471}
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
float bpm_t
The BPM type.
Definition types.h:70
@ Snapshot
Creates a snapshot of the object with the same identity.
Definition icloneable.h:23