Loading...
Searching...
No Matches
Concepts.hpp
1#pragma once
2#include <Process/Dataflow/WidgetInlets.hpp>
3#include <Process/ProcessFlags.hpp>
4#include <Process/ProcessMetadata.hpp>
5
6#include <Dataflow/CurveInlet.hpp>
7#include <Engine/Node/CommonWidgets.hpp>
8
9#include <score/plugins/UuidKey.hpp>
10
11#include <ossia/dataflow/audio_port.hpp>
12#include <ossia/dataflow/port.hpp>
13#include <ossia/dataflow/safe_nodes/tick_policies.hpp>
14
15#include <boost/container/vector.hpp>
16
17#include <avnd/binding/ossia/dynamic_ports.hpp>
18#include <avnd/binding/ossia/qt.hpp>
19#include <avnd/binding/ossia/to_value.hpp>
20#include <avnd/binding/ossia/uuid.hpp>
21#include <avnd/common/concepts_polyfill.hpp>
22#include <avnd/common/struct_reflection.hpp>
23#include <avnd/concepts/audio_port.hpp>
24#include <avnd/concepts/channels.hpp>
25#include <avnd/concepts/curve.hpp>
26#include <avnd/concepts/folder_items.hpp>
27#include <avnd/concepts/gfx.hpp>
28#include <avnd/concepts/midi_port.hpp>
29#include <avnd/concepts/parameter.hpp>
30#include <avnd/introspection/input.hpp>
31#include <avnd/wrappers/metadatas.hpp>
32#include <avnd/wrappers/widgets.hpp>
33
34#include <cmath>
35
36#include <span>
37#include <type_traits>
38
39#define make_uuid(text) score::uuids::string_generator::compute((text))
40
41namespace oscr
42{
47template <typename T>
49 = oscr::has_controller_ports<T> || oscr::has_dynamic_ports<T>
50 || (avnd::controller_setup_port_input_introspection<T>::size > 0);
51
52template <typename Node, typename FieldIndex>
53struct CustomFloatControl;
54
55template <typename BaseInletType, typename Node, typename T, typename FieldIndex>
56struct CustomGenericControl;
57}
58
59template <typename Node, typename FieldIndex>
60struct is_custom_serialized<oscr::CustomFloatControl<Node, FieldIndex>> : std::true_type
61{
62};
63
64template <typename BaseInletType, typename Node, typename T, typename FieldIndex>
65struct is_custom_serialized<
66 oscr::CustomGenericControl<BaseInletType, Node, T, FieldIndex>> : std::true_type
67{
68};
69
70namespace oscr
71{
72
74{
75 using Process::ControlInlet::ControlInlet;
76
78 float min, float max, float init, const QString& name, Id<Process::Port> id,
79 QObject* parent)
80 : ControlInlet{name, id, parent}
81 {
82 displayHandledExplicitly = true;
83 setValue(init);
84 setInit(init);
85 setDomain(ossia::make_domain(min, max));
86 }
87
88 auto getMin() const noexcept { return domain().get().template convert_min<float>(); }
89 auto getMax() const noexcept { return domain().get().template convert_max<float>(); }
90
91 void setupExecution(ossia::inlet& inl, QObject* exec_context) const noexcept override
92 {
93 auto& port = **safe_cast<ossia::value_inlet*>(&inl);
94 port.type = ossia::val_type::FLOAT;
95 port.domain = domain().get();
96 }
97};
98
99template <typename Node, typename FieldIndex>
101{
102 using CustomFloatControlBase::CustomFloatControlBase;
103
104 static key_type static_concreteKey() noexcept
105 {
106 return make_field_uuid<Node>(true, FieldIndex{});
107 }
108 key_type concreteKey() const noexcept override { return static_concreteKey(); }
109
110 void serialize_impl(const VisitorVariant& vis) const noexcept override
111 {
112 score::serialize_dyn(vis, *this);
113 }
114
115 ~CustomFloatControl() = default;
116};
117
118template <typename BaseInletType, typename Node, typename T, typename FieldIndex>
119struct CustomGenericControl : public BaseInletType
120{
121 using BaseInletType::BaseInletType;
122 void setValue(const ossia::value& value) override
123 {
124 if constexpr(requires { T::migrate_value(value); })
125 BaseInletType::setValue(T::migrate_value(value));
126 else
127 BaseInletType::setValue(value);
128 }
129
130 static BaseInletType::key_type static_concreteKey() noexcept
131 {
132 return make_field_uuid<Node>(true, FieldIndex{});
133 }
134 BaseInletType::key_type concreteKey() const noexcept override
135 {
136 return static_concreteKey();
137 }
138
139 void serialize_impl(const VisitorVariant& vis) const noexcept override
140 {
141 score::serialize_dyn(vis, *this);
142 }
143
144 ~CustomGenericControl() = default;
145};
146
147template <typename BaseInletType, typename Node, typename T, typename FieldIndex>
148using ControlTypeToUse = std::conditional_t<
149 avnd::controller_interaction_port<T>,
151}
152template <typename Node, typename FieldIndex>
153struct TSerializer<DataStream, oscr::CustomFloatControl<Node, FieldIndex>>
154{
156 static void readFrom(DataStream::Serializer& s, const model_type& p)
157 {
158 s.read((const Process::ControlInlet&)p);
159 }
160
161 static void writeTo(DataStream::Deserializer& s, model_type& eff) { }
162};
163
164template <typename Node, typename FieldIndex>
165struct TSerializer<JSONObject, oscr::CustomFloatControl<Node, FieldIndex>>
166{
168 static void readFrom(JSONObject::Serializer& s, const model_type& p)
169 {
170 s.read((const Process::ControlInlet&)p);
171 }
172
173 static void writeTo(JSONObject::Deserializer& s, model_type& eff) { }
174};
175
176template <typename BaseInletType, typename Node, typename T, typename FieldIndex>
178 DataStream, oscr::CustomGenericControl<BaseInletType, Node, T, FieldIndex>>
179{
181 static void readFrom(DataStream::Serializer& s, const model_type& p)
182 {
183 s.read((const BaseInletType&)p);
184 }
185
186 static void writeTo(DataStream::Deserializer& s, model_type& eff) { }
187};
188
189template <typename BaseInletType, typename Node, typename T, typename FieldIndex>
191 JSONObject, oscr::CustomGenericControl<BaseInletType, Node, T, FieldIndex>>
192{
194 static void readFrom(JSONObject::Serializer& s, const model_type& p)
195 {
196 s.read((const BaseInletType&)p);
197 }
198
199 static void writeTo(JSONObject::Deserializer& s, model_type& eff) { }
200};
201
202namespace oscr
203{
204
205template <typename Node, typename T, std::size_t N>
206static inline auto
207make_control_in(avnd::field_index<N>, Id<Process::Port>&& id, QObject* parent)
208{
209 using value_type = as_type(T::value);
210 constexpr auto name = avnd::get_name<T>();
211 QString qname = QString::fromUtf8(name.data(), name.size());
212
213 constexpr auto widg = avnd::get_widget<T>();
214
215 // FIXME log normalization & friends
216
217 if constexpr(avnd::curve_port<T>)
218 {
219 return new Dataflow::CurveInlet(id, parent);
220 }
221 else if constexpr(widg.widget == avnd::widget_type::bang)
222 {
223 return new Process::ImpulseButton{qname, id, parent};
224 }
225 else if constexpr(widg.widget == avnd::widget_type::button)
226 {
227 return new Process::Button{qname, id, parent};
228 }
229 else if constexpr(widg.widget == avnd::widget_type::toggle)
230 {
231 static constexpr auto c = avnd::get_range<T>();
232 if constexpr(requires { c.values(); })
233 {
234 return new Process::ChooserToggle{
235 {c.values[0], c.values[1]}, c.init, qname, id, parent};
236 }
237 else
238 {
239 return new Process::Toggle{bool(c.init), qname, id, parent};
240 }
241 }
242 else if constexpr(widg.widget == avnd::widget_type::slider)
243 {
244 static constexpr auto c = avnd::get_range<T>();
245 if constexpr(std::is_integral_v<value_type>)
246 {
247 return new Process::IntSlider{c.min, c.max, c.init, qname, id, parent};
248 }
249 else
250 {
251 if constexpr(avnd::has_mapper<T>)
252 {
253 return new CustomFloatControl<Node, avnd::field_index<N>>{c.min, c.max, c.init,
254 qname, id, parent};
255 }
256 else
257 {
258 return new Process::FloatSlider{c.min, c.max, c.init, qname, id, parent};
259 }
260 }
261 }
262 else if constexpr(widg.widget == avnd::widget_type::log_slider)
263 {
264 static constexpr auto c = avnd::get_range<T>();
265 return new Process::LogFloatSlider{c.min, c.max, c.init, qname, id, parent};
266 }
267 else if constexpr(widg.widget == avnd::widget_type::log_knob)
268 {
269 // FIXME
270 static constexpr auto c = avnd::get_range<T>();
271 return new Process::LogFloatSlider{c.min, c.max, c.init, qname, id, parent};
272 }
273 else if constexpr(widg.widget == avnd::widget_type::time_chooser)
274 {
275 static constexpr auto c = avnd::get_range<T>();
276 return new Process::TimeChooser{c.min, c.max, c.init, qname, id, parent};
277 }
278 else if constexpr(widg.widget == avnd::widget_type::folder)
279 {
280 if constexpr(avnd::has_range<T>) {
281 static constexpr auto c = avnd::get_range<T>();
282#if defined(_MSC_VER)
283 auto init = std::begin(c.init);
284 if constexpr(std::is_same_v<std::decay_t<decltype(init)>, const char*>)
285 return new Process::FolderChooser{QString::fromUtf8(init, std::size(c.init)), qname, id, parent};
286 else
287 return new Process::FolderChooser{QString::fromUtf8(c.init.data(), c.init.size()), qname, id, parent};
288#else
289 return new Process::FolderChooser{
290 QString::fromUtf8(&*std::begin(c.init), std::size(c.init)), qname, id, parent};
291#endif
292 } else {
293 return new Process::FolderChooser{"", qname, id, parent};
294 }
295 }
296 else if constexpr(widg.widget == avnd::widget_type::range_slider)
297 {
298 static constexpr auto c = avnd::get_range<T>();
299 if constexpr(std::is_integral_v<value_type>)
300 {
301 auto [start, end] = c.init;
302 return new Process::IntRangeSlider{c.min, c.max, {(float)start, (float)end},
303 qname, id, parent};
304 }
305 else
306 {
307 auto [start, end] = c.init;
308 return new Process::FloatRangeSlider{c.min, c.max, {(float)start, (float)end},
309 qname, id, parent};
310 }
311 }
312 else if constexpr(widg.widget == avnd::widget_type::range_spinbox)
313 {
314 static constexpr auto c = avnd::get_range<T>();
315 if constexpr(std::is_integral_v<value_type>)
316 {
317 auto [start, end] = c.init;
318 return new Process::IntRangeSpinBox{c.min, c.max, {(float)start, (float)end},
319 qname, id, parent};
320 }
321 else
322 {
323 auto [start, end] = c.init;
324 return new Process::FloatRangeSpinBox{c.min, c.max, {(float)start, (float)end},
325 qname, id, parent};
326 }
327 }
328 else if constexpr(widg.widget == avnd::widget_type::string_list)
329 {
330 auto p
331 = new CustomGenericControl<Process::MultiSlider, Node, T, avnd::field_index<N>>{
332 oscr::to_ossia_value(T{}.value), qname, id, parent};
333 p->setDomain(State::Domain{});
334 return p;
335 }
336 else if constexpr(widg.widget == avnd::widget_type::multi_slider)
337 {
338 std::vector<ossia::value> init;
339 return new Process::MultiSlider{init, qname, id, parent};
340 }
341 else if constexpr(widg.widget == avnd::widget_type::multi_slider_xy)
342 {
343 std::vector<ossia::value> init;
344 return new Process::MultiSliderXY{init, qname, id, parent};
345 }
346 else if constexpr(widg.widget == avnd::widget_type::path_generator_xy)
347 {
348 std::vector<ossia::value> init;
349 return new Process::PathGeneratorXY{init, qname, id, parent};
350 }
351 else if constexpr(widg.widget == avnd::widget_type::spinbox)
352 {
353 static constexpr auto c = avnd::get_range<T>();
354 if constexpr(std::is_integral_v<value_type>)
355 {
356 return new ControlTypeToUse<Process::IntSpinBox, Node, T, avnd::field_index<N>>{
357 c.min, c.max, c.init, qname, id, parent};
358 }
359 else
360 {
361 return new Process::FloatSpinBox{c.min, c.max, c.init, qname, id, parent};
362 }
363 }
364 else if constexpr(widg.widget == avnd::widget_type::knob)
365 {
366 static constexpr auto c = avnd::get_range<T>();
367 if constexpr(std::is_integral_v<value_type>)
368 {
369 // FIXME do a IntKnob
370 return new Process::IntSlider{c.min, c.max, c.init, qname, id, parent};
371 }
372 else
373 {
374 if constexpr(avnd::has_mapper<T>)
375 {
376 return new CustomFloatControl<Node, avnd::field_index<N>>{c.min, c.max, c.init,
377 qname, id, parent};
378 }
379 else
380 {
381 return new Process::FloatKnob{c.min, c.max, c.init, qname, id, parent};
382 }
383 }
384 }
385 else if constexpr(widg.widget == avnd::widget_type::lineedit)
386 {
387 if constexpr(avnd::has_range<T>)
388 {
389 static constexpr auto c = avnd::get_range<T>();
390 if constexpr(avnd::program_parameter<T>)
391 {
392 auto p = new Process::ProgramEdit{c.init.data(), qname, id, parent};
393 p->language = T::language();
394 return p;
395 }
396 else
397 return new ControlTypeToUse<Process::LineEdit, Node, T, avnd::field_index<N>>{
398 c.init.data(), qname, id, parent};
399 }
400 else
401 {
402 if constexpr(avnd::program_parameter<T>)
403 {
404 auto p = new Process::ProgramEdit{"", qname, id, parent};
405 p->language = T::language();
406 return p;
407 }
408 else
409 return new ControlTypeToUse<Process::LineEdit, Node, T, avnd::field_index<N>>{
410 "", qname, id, parent};
411 }
412 }
413 else if constexpr(avnd::folder_items_parameter<T>)
414 {
415 // Folder-backed, string-valued combobox: the value IS the selected file
416 // name, so an object uses it directly like a file-name string. Items are
417 // filled from the sibling folder port at edit time by the model.
418 static constexpr auto c = avnd::get_range<T>();
419 const std::string initv{c.init};
420 std::vector<std::pair<QString, ossia::value>> initAlts;
421 initAlts.emplace_back(QString::fromStdString(initv), initv);
422 auto combo = new Process::ComboBox{
423 std::move(initAlts), ossia::value{initv}, qname, id, parent};
424 combo->folderPortName
425 = QString::fromUtf8(T::folder_port().data(), T::folder_port().size());
426 // extensions(): space-separated suffixes ("wav aif json") -> "*.wav" ...
427 const std::string_view ex = T::extensions();
428 std::size_t pos = 0;
429 while(pos < ex.size())
430 {
431 auto sp = ex.find(' ', pos);
432 if(sp == std::string_view::npos)
433 sp = ex.size();
434 if(sp > pos)
435 {
436 auto suffix = QString::fromUtf8(ex.data() + pos, int(sp - pos));
437 // Both "wav" and ".wav" are natural to write; normalise so neither
438 // turns into the glob "*..wav", which matches nothing.
439 while(suffix.startsWith('.'))
440 suffix.remove(0, 1);
441 if(!suffix.isEmpty())
442 combo->fileExtensions.push_back("*." + suffix);
443 }
444 pos = sp + 1;
445 }
446 return combo;
447 }
448 else if constexpr(widg.widget == avnd::widget_type::combobox)
449 {
450 static constexpr auto c = avnd::get_range<T>();
451 using init_type = std::decay_t<decltype(c.init)>;
452 ossia::value init;
453 if constexpr(std::is_integral_v<init_type> || std::is_enum_v<init_type>)
454 init = static_cast<int>(c.init);
455 else
456 init = c.init;
457 return new Process::ComboBox{
458 to_combobox_range(c.values), std::move(init), qname, id, parent};
459 }
460 else if constexpr(widg.widget == avnd::widget_type::choices)
461 {
462 static constexpr auto c = avnd::get_range<T>();
463 auto enums = avnd::to_enum_range(c.values);
464 static_assert(
465 std::is_integral_v<decltype(c.init)> || std::is_enum_v<decltype(c.init)>);
466 auto init = enums[static_cast<int>(c.init)];
467 std::vector<QString> pixmaps;
468 if constexpr(avnd::has_pixmaps<T>)
469 {
470 for(std::string_view pix : avnd::get_pixmaps<T>())
471 {
472 pixmaps.push_back(QString::fromLatin1(pix.data(), pix.size()));
473 }
474 }
475 return new Process::Enum{
476 std::move(enums), pixmaps, std::move(init), qname, id, parent};
477 }
478 else if constexpr(widg.widget == avnd::widget_type::xy)
479 {
480 static constexpr auto c = avnd::get_range<T>();
481 if constexpr(requires {
482 c.min == 0.f;
483 c.max == 0.f;
484 c.init == 0.f;
485 })
486 {
487 return new Process::XYSlider{
488 {c.min, c.min}, {c.max, c.max}, {c.init, c.init}, qname, id, parent};
489 }
490 else
491 {
492 auto [mx, my] = c.min;
493 auto [Mx, My] = c.max;
494 auto [ix, iy] = c.init;
495 return new Process::XYSlider{{mx, my}, {Mx, My}, {ix, iy}, qname, id, parent};
496 }
497 }
498 else if constexpr(widg.widget == avnd::widget_type::xyz)
499 {
500 static constexpr auto c = avnd::get_range<T>();
501 if constexpr(requires {
502 c.min == 0.f;
503 c.max == 0.f;
504 c.init == 0.f;
505 })
506 {
507 return new Process::XYZSlider{
508 {c.min, c.min, c.min},
509 {c.max, c.max, c.max},
510 {c.init, c.init, c.init},
511 qname,
512 id,
513 parent};
514 }
515 else
516 {
517 auto [mx, my, mz] = c.min;
518 auto [Mx, My, Mz] = c.max;
519 auto [ix, iy, iz] = c.init;
520 return new Process::XYZSlider{{mx, my, mz}, {Mx, My, Mz}, {ix, iy, iz},
521 qname, id, parent};
522 }
523 }
524 else if constexpr(widg.widget == avnd::widget_type::xy_spinbox)
525 {
526 using data_type = std::decay_t<decltype(value_type{}.x)>;
527 static constexpr auto c = avnd::get_range<T>();
528 if constexpr(requires {
529 c.min == 0.f;
530 c.max == 0.f;
531 c.init == 0.f;
532 })
533 {
534 return new Process::XYSpinboxes{
535 {c.min, c.min},
536 {c.max, c.max},
537 {c.init, c.init},
538 std::is_integral_v<data_type>,
539 qname,
540 id,
541 parent};
542 }
543 else
544 {
545 auto [mx, my] = c.min;
546 auto [Mx, My] = c.max;
547 auto [ix, iy] = c.init;
548 return new Process::XYSpinboxes{
549 {mx, my}, {Mx, My}, {ix, iy}, std::is_integral_v<data_type>,
550 qname, id, parent};
551 }
552 }
553 else if constexpr(widg.widget == avnd::widget_type::xyz_spinbox)
554 {
555 static constexpr auto c = avnd::get_range<T>();
556 if constexpr(requires {
557 c.min == 0.f;
558 c.max == 0.f;
559 c.init == 0.f;
560 })
561 {
562 return new Process::XYZSpinboxes{
563 {c.min, c.min, c.min},
564 {c.max, c.max, c.max},
565 {c.init, c.init, c.init},
566 false,
567 qname,
568 id,
569 parent};
570 }
571 else
572 {
573 auto [mx, my, mz] = c.min;
574 auto [Mx, My, Mz] = c.max;
575 auto [ix, iy, iz] = c.init;
576 return new Process::XYZSpinboxes{{mx, my, mz}, {Mx, My, Mz}, {ix, iy, iz},
577 qname, id, parent};
578 }
579 }
580 else if constexpr(widg.widget == avnd::widget_type::color)
581 {
582 static constexpr auto c = avnd::get_range<T>();
583 static constexpr auto i = c.init;
584 return new Process::HSVSlider{{i.r, i.g, i.b, i.a}, qname, id, parent};
585 }
586 else if constexpr(widg.widget == avnd::widget_type::control)
587 {
588 return new Process::ControlInlet{qname, id, parent};
589 }
590 else if constexpr(widg.widget == avnd::widget_type::no_control)
591 {
592 return new Process::ValueInlet{qname, id, parent};
593 }
594 else
595 {
596 static_assert(T::is_not_a_valid_control);
597 }
598}
599
600template <typename T, std::size_t N>
601static inline auto
602make_control_out(avnd::field_index<N>, Id<Process::Port>&& id, QObject* parent)
603{
604 constexpr auto name = avnd::get_name<T>();
605 constexpr auto widg = avnd::get_widget<T>();
606 QString qname = QString::fromUtf8(name.data(), name.size());
607
608 // FIXME log normalization & friends
609
610 if constexpr(widg.widget == avnd::widget_type::bargraph)
611 {
612 static constexpr auto c = avnd::get_range<T>();
613 return new Process::Bargraph{c.min, c.max, c.init, qname, id, parent};
614 }
615 else if constexpr(widg.widget == avnd::widget_type::control)
616 {
617 return new Process::ControlOutlet{qname, id, parent};
618 }
619 else if constexpr(widg.widget == avnd::widget_type::no_control)
620 {
621 return new Process::ValueOutlet{qname, id, parent};
622 }
623 else if constexpr(avnd::fp_ish<decltype(T::value)>)
624 {
625 static constexpr auto c = avnd::get_range<T>();
626 return new Process::Bargraph{c.min, c.max, c.init, qname, id, parent};
627 }
628 else
629 {
630 return new Process::ControlOutlet{qname, id, parent};
631 }
632}
633
634template <typename T>
635static inline constexpr auto make_control_out(const T& t)
636{
637 return make_control_out<T>();
638}
639}
Definition VisitorInterface.hpp:53
Definition DataStreamVisitor.hpp:27
Definition DataStreamVisitor.hpp:202
Definition VisitorInterface.hpp:61
Definition JSONVisitor.hpp:52
Definition JSONVisitor.hpp:423
Definition Port.hpp:218
Definition Port.hpp:452
Definition Port.hpp:518
Definition Port.hpp:544
Definition UuidKey.hpp:345
The id_base_t class.
Definition Identifier.hpp:59
Definition Concepts.hpp:49
Definition Controls.hpp:27
Definition CurveInlet.hpp:47
Definition WidgetInlets.hpp:674
Definition WidgetInlets.hpp:515
Definition WidgetInlets.hpp:337
Definition WidgetInlets.hpp:446
Definition WidgetInlets.hpp:491
Definition WidgetInlets.hpp:174
Definition WidgetInlets.hpp:235
Definition WidgetInlets.hpp:267
Definition WidgetInlets.hpp:160
Definition WidgetInlets.hpp:298
Definition WidgetInlets.hpp:395
Definition WidgetInlets.hpp:539
Definition WidgetInlets.hpp:526
Definition WidgetInlets.hpp:219
Definition WidgetInlets.hpp:251
Definition WidgetInlets.hpp:204
Definition WidgetInlets.hpp:189
Definition WidgetInlets.hpp:629
Definition WidgetInlets.hpp:644
Definition WidgetInlets.hpp:658
Definition WidgetInlets.hpp:433
Definition WidgetInlets.hpp:312
Definition WidgetInlets.hpp:326
Definition WidgetInlets.hpp:553
Definition WidgetInlets.hpp:587
Definition WidgetInlets.hpp:570
Definition WidgetInlets.hpp:611
Definition Domain.hpp:17
Definition VisitorInterface.hpp:13
The VisitorVariant struct.
Definition VisitorInterface.hpp:26
Definition Concepts.hpp:74
Definition Concepts.hpp:101
Definition Concepts.hpp:120