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