Loading...
Searching...
No Matches
score-plugin-avnd/Crousti/ProcessModel.hpp
1#pragma once
2#include <Process/GenericProcessFactory.hpp>
3#include <Process/Process.hpp>
4#include <Process/ProcessFactory.hpp>
5
6#include <Crousti/Concepts.hpp>
7#include <Crousti/MessageBus.hpp>
8#include <Crousti/Metadata.hpp>
9#include <Crousti/Metadatas.hpp>
10#include <Crousti/ProcessModelPortInit.hpp>
11#include <Dataflow/Commands/CableHelpers.hpp>
12
13#include <score/serialization/MapSerialization.hpp>
14#include <score/tools/std/HashMap.hpp>
15
16#include <core/document/Document.hpp>
17
18#include <ossia/detail/string_map.hpp>
19#include <ossia/detail/type_if.hpp>
20#include <ossia/detail/typelist.hpp>
21
22#include <boost/pfr.hpp>
23
24#include <QTimer>
25
26#include <avnd/binding/ossia/data_node.hpp>
27#include <avnd/binding/ossia/dynamic_ports.hpp>
28#include <avnd/common/for_nth.hpp>
29#include <avnd/concepts/gfx.hpp>
30#include <avnd/concepts/ui.hpp>
31#include <avnd/introspection/messages.hpp>
32#include <avnd/wrappers/bus_host_process_adapter.hpp>
33
34#include <score_plugin_engine.hpp>
35
40namespace oscr
41{
42
43template <typename Info>
45{
46};
47
48template <typename Info>
52
53namespace
54{
55struct dummy_ui_callback
56{
57 void operator()(const QByteArray& arr) noexcept { }
58};
59}
60
61template <avnd::has_processor_to_gui_bus Info>
63{
64 std::function<void(QByteArray)> to_ui = dummy_ui_callback{};
65};
66
67template <avnd::has_gui_to_processor_bus Info>
69{
70 std::function<void(QByteArray)> from_ui = dummy_ui_callback{};
71};
72
73inline void hideAllInlets(Process::ProcessModel& proc)
74{
75 for(auto& p : proc.inlets())
76 p->hidden = true;
77 for(auto& p : proc.outlets())
78 p->hidden = true;
79}
80
81template <typename Info>
82class ProcessModel final
84 , public MessageBusWrapperFromUi<Info>
85 , public MessageBusWrapperToUi<Info>
86{
87 SCORE_SERIALIZE_FRIENDS
88 PROCESS_METADATA_IMPL(ProcessModel<Info>)
89 friend struct TSerializer<DataStream, oscr::ProcessModel<Info>>;
90 friend struct TSerializer<JSONObject, oscr::ProcessModel<Info>>;
91
92public:
93 [[no_unique_address]]
94 oscr::dynamic_ports_storage<Info> dynamic_ports;
95
96 [[no_unique_address]]
97 ossia::type_if<Info, oscr::has_dynamic_ports<Info>> object_storage_for_ports_callbacks;
98
100 const TimeVal& duration, const Id<Process::ProcessModel>& id,
101 const score::DocumentContext& ctx, QObject* parent)
103 duration, id, Metadata<ObjectKey_k, ProcessModel>::get(), parent}
104 {
105 metadata().setInstanceName(*this);
106
107 init_common();
108 init_before_port_creation();
109 init_all_ports();
110 init_after_port_creation();
111 }
112
114 const TimeVal& duration, const QString& custom,
115 const Id<Process::ProcessModel>& id, QObject* parent)
117 duration, id, Metadata<ObjectKey_k, ProcessModel>::get(), parent}
118 {
119 metadata().setInstanceName(*this);
120
121 init_common();
122 init_before_port_creation();
123 init_all_ports();
124
125 if constexpr(avnd::file_input_introspection<Info>::size > 0)
126 {
127 static constexpr auto idx
128 = avnd::file_input_introspection<Info>::index_to_field_index(0);
129 setupInitialStringPort(idx, custom);
130 }
131 else if constexpr(avnd::control_input_introspection<Info>::size > 0)
132 {
133 static constexpr auto idx
134 = avnd::control_input_introspection<Info>::index_to_field_index(0);
135 using type =
136 typename avnd::control_input_introspection<Info>::template nth_element<0>;
137 if constexpr(avnd::string_ish<decltype(type::value)>)
138 setupInitialStringPort(idx, custom);
139 }
140 init_after_port_creation();
141 }
142
143 void setupInitialStringPort(int idx, const QString& custom) noexcept
144 {
145 Process::Inlet* port = avnd_input_idx_to_model_ports(idx)[0];
146 auto pp = safe_cast<Process::ControlInlet*>(port);
147
148 if(auto val = pp->value(); bool(val.target<std::string>()))
149 {
150 pp->setValue(custom.toStdString());
151 }
152 }
153
154 template <typename Impl>
155 explicit ProcessModel(Impl& vis, QObject* parent)
156 : Process::ProcessModel{vis, parent}
157 {
158 init_common();
159 init_before_port_creation();
160 vis.writeTo(*this);
161 check_all_ports();
162 init_after_port_creation();
163 }
164
165 ~ProcessModel() override { }
166
167private:
168 void init_common()
169 {
170 if constexpr(avnd::tag_loops_by_default<Info>)
171 setLoops(true);
172 }
173
174 void init_before_port_creation() { init_dynamic_ports(); }
175 void init_after_port_creation() { init_controller_ports(); }
176 void check_all_ports()
177 {
178 if(std::ssize(m_inlets) != expected_input_ports()
179 || std::ssize(m_outlets) != expected_output_ports())
180 {
181 qDebug() << typeid(Info).name() << this->metadata().getName()
182 << ": WARNING : process does not match spec: I " << m_inlets.size()
183 << "but expected: " << expected_input_ports() << " ; O "
184 << m_outlets.size() << "but expected: " << expected_output_ports();
185
186 std::vector<Dataflow::SavedPort> m_oldInlets, m_oldOutlets;
187 for(auto& port : m_inlets)
188 m_oldInlets.emplace_back(
189 Dataflow::SavedPort{port->name(), port->type(), port->saveData()});
190 for(auto& port : m_outlets)
191 m_oldOutlets.emplace_back(
192 Dataflow::SavedPort{port->name(), port->type(), port->saveData()});
193
194 qDeleteAll(m_inlets);
195 m_inlets.clear();
196 qDeleteAll(m_outlets);
197 m_outlets.clear();
198
199 init_all_ports();
200
201 Dataflow::reloadPortsInNewProcess(m_oldInlets, m_oldOutlets, *this);
202 }
203 }
204
205 void init_controller_ports()
206 {
207 if constexpr(
208 avnd::dynamic_ports_input_introspection<Info>::size > 0
209 || avnd::dynamic_ports_output_introspection<Info>::size > 0)
210 {
211 avnd::control_input_introspection<Info>::for_all_n2(
212 avnd::get_inputs<Info>((Info&)this->object_storage_for_ports_callbacks),
213 [this]<std::size_t Idx, typename F>(
214 F& field, auto pred_index, avnd::field_index<Idx>) {
215 Info& obj = this->object_storage_for_ports_callbacks;
216 if constexpr(requires { F::on_controller_setup(); })
217 {
218 auto controller_inlets = avnd_input_idx_to_model_ports(Idx);
219 SCORE_ASSERT(controller_inlets.size() == 1);
220 auto inlet = qobject_cast<Process::ControlInlet*>(controller_inlets[0]);
221
222 oscr::from_ossia_value(inlet->value(), field.value);
223
224 if_possible(field.update(obj));
225
226 F::on_controller_setup()(obj, field.value);
227 }
228 if constexpr(requires { F::on_controller_interaction(); })
229 {
230 auto controller_inlets = avnd_input_idx_to_model_ports(Idx);
231 SCORE_ASSERT(controller_inlets.size() == 1);
232 auto inlet = qobject_cast<Process::ControlInlet*>(controller_inlets[0]);
233 inlet->noValueChangeOnMove = true;
234
235 if constexpr(!requires { F::on_controller_setup(); })
236 {
237 oscr::from_ossia_value(inlet->value(), field.value);
238 if_possible(field.update(obj));
239 F::on_controller_interaction()(obj, field.value);
240 }
241
242 connect(
243 inlet, &Process::ControlInlet::valueChanged,
244 [this, &field](const ossia::value& val) {
245 Info& obj = this->object_storage_for_ports_callbacks;
246 oscr::from_ossia_value(val, field.value);
247
248 if_possible(field.update(obj));
249
250 F::on_controller_interaction()(obj, field.value);
251 });
252 }
253 });
254
255 if constexpr(avnd::has_gui_to_processor_bus<Info>)
256 {
257 // FIXME needs to be a list of callbacks?
258 }
259
260 if constexpr(avnd::has_processor_to_gui_bus<Info>)
261 {
262 Info& obj = this->object_storage_for_ports_callbacks;
263
264 obj.send_message = [this]<typename T>(T&& b) mutable {
265 if(this->to_ui)
266 MessageBusSender{this->to_ui}(std::move(b));
267 };
268 }
269 }
270 }
271
272 void init_dynamic_ports()
273 {
274 // To check:
275
276 // - serialization
277 // - OK: uses of total_input_count / total_output_count
278
279 // - OK: uses of oscr::modelPort (btw this does not seem to match total_input_count)
280
281 // - execution, resize the avnd object with the number of ports
282 // -> connect controls so that they change in the ui
283
284 // - OK: Layer::createControl: uses index_in_struct and assumes it's a ProcessModel::inlet index
285
286 // How are things going to happen UI-wise: all controls created one after each other ?
287 // what if we want to create separate tabs with e.g.
288 // control A 1, control B 1
289 // control A 2, control B 2
290 // we need to know the current state of dynamic ports
291
292 // UI: recreating UI causes crash when editing e.g. context menu of spinbox
293 // UI: recreating UI causes bug when editing e.g. spinbox
294
295 // -> we have to find a way to detect that the currently edited object is being deleted
296
297 // other option: mark widget as "unsafe", do not have it send value changes until mouse is released
298
299 // REloading on crash: does crash, because the control change does not have
300 // the time to be applied. In DocumentBuilder::loadCommandStack we have
301 // to process the event loop in-between events.
302 // FIxed if we put zero but then changing the widget with the mouses crashes because of the comment
303 // below...
304 // ->
305 // terminate called after throwing an instance of 'std::runtime_error'
306 // what(): Ongoing command mismatch: current command SetControlValue does not match new command MoveNodes
307
308 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
309 {
310 Info& obj = object_storage_for_ports_callbacks;
311 avnd::dynamic_ports_input_introspection<Info>::for_all_n2(
312 avnd::get_inputs(obj),
313 [&]<std::size_t N>(auto& port, auto pred_idx, avnd::field_index<N> field_idx) {
314 port.request_port_resize = [this, &port](int new_count) {
315 // We're in the ui thread, we can just push the request directly.
316 // With some delay as otherwise we may be deleting the widget we
317 // are clicking on before mouse release and Qt really doesn't like that
318 // But when we are loading the document we actually do not want the
319 // delay to make sure the port is created before the cable connects to it
320 if(score::IDocument::documentFromObject(*this)->loaded())
321 {
322 QTimer::singleShot(0, this, [self = QPointer{this}, &port, new_count] {
323 if(self)
324 self->request_new_dynamic_input_count(
325 port, avnd::field_index<N>{}, new_count);
326 });
327 }
328 else
329 {
330 this->request_new_dynamic_input_count(
331 port, avnd::field_index<N>{}, new_count);
332 }
333 };
334 });
335 }
336
337 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
338 {
339 Info& obj = object_storage_for_ports_callbacks;
340 avnd::dynamic_ports_output_introspection<Info>::for_all_n2(
341 avnd::get_outputs(obj),
342 [&]<std::size_t N>(auto& port, auto pred_idx, avnd::field_index<N> field_idx) {
343 port.request_port_resize = [this, &port](int new_count) {
344 // See comment above for inputs
345 if(score::IDocument::documentFromObject(*this)->loaded())
346 {
347 QTimer::singleShot(0, this, [self = QPointer{this}, &port, new_count] {
348 if(self)
349 self->request_new_dynamic_output_count(
350 port, avnd::field_index<N>{}, new_count);
351 });
352 }
353 else
354 {
355 this->request_new_dynamic_output_count(
356 port, avnd::field_index<N>{}, new_count);
357 }
358 };
359 });
360 }
361 }
362
363 template <typename P, std::size_t N>
364 void request_new_dynamic_input_count(P& port, avnd::field_index<N> idx, int count)
365 {
366 const int current_model_ports = dynamic_ports.num_in_ports(idx);
367 if(current_model_ports == count || count < 0 || count > 512)
368 return;
369
370 ossia::small_pod_vector<Process::Inlet*, 4> to_delete;
371 if(current_model_ports < count)
372 {
373 // Add new ports
374 // 1. Find the location where to add them
375 auto res = avnd_input_idx_to_iterator(idx);
376 res += current_model_ports;
377 Process::Inlets inlets_to_add;
378 InletInitFunc<Info> inlets{*this, inlets_to_add};
379 inlets.inlet = 10000 + N * 1000 + current_model_ports;
380 int sz = 0;
381 for(int i = current_model_ports; i < count; i++)
382 {
383 inlets(port, idx);
384 if(std::ssize(inlets_to_add) > sz)
385 {
386 sz = std::ssize(inlets_to_add);
387 auto new_inlet = inlets_to_add.back();
388 if(auto nm = new_inlet->name(); nm.contains("{}"))
389 {
390 nm.replace("{}", QString::number(i));
391 new_inlet->setName(nm);
392 }
393 }
394 }
395 m_inlets.insert(res, inlets_to_add.begin(), inlets_to_add.end());
396 }
397 else if(current_model_ports > count)
398 {
399 // Delete the ports
400 auto res = avnd_input_idx_to_iterator(idx);
401 res += count;
402 auto begin_deleted = res;
403 for(int i = 0; i < (current_model_ports - count); i++)
404 {
405 to_delete.push_back(*res);
406 ++res;
407 }
408 m_inlets.erase(begin_deleted, res);
409 }
410
411 dynamic_ports.num_in_ports(idx) = count;
412
413 inletsChanged();
414 for(auto port : to_delete)
415 delete port;
416 }
417
418 template <typename P, std::size_t N>
419 void request_new_dynamic_output_count(P& port, avnd::field_index<N> idx, int count)
420 {
421 const int current_model_ports = dynamic_ports.num_out_ports(idx);
422 if(current_model_ports == count || count < 0 || count > 512)
423 return;
424
425 ossia::small_pod_vector<Process::Outlet*, 4> to_delete;
426 if(current_model_ports < count)
427 {
428 // Add new ports
429 // 1. Find the location where to add them
430 auto res = avnd_output_idx_to_iterator(idx);
431 res += current_model_ports;
432 Process::Outlets outlets_to_add;
433 OutletInitFunc<Info> outlets{*this, outlets_to_add};
434 outlets.outlet = 1000000 + N * 1000 + current_model_ports;
435 int sz = 0;
436 for(int i = current_model_ports; i < count; i++)
437 {
438 outlets(port, idx);
439 if(std::ssize(outlets_to_add) > sz)
440 {
441 sz = std::ssize(outlets_to_add);
442 auto new_outlet = outlets_to_add.back();
443 if(auto nm = new_outlet->name(); nm.contains("{}"))
444 {
445 nm.replace("{}", QString::number(i));
446 new_outlet->setName(nm);
447 }
448 }
449 }
450 m_outlets.insert(res, outlets_to_add.begin(), outlets_to_add.end());
451 }
452 else if(current_model_ports > count)
453 {
454 // Delete the ports
455 auto res = avnd_output_idx_to_iterator(idx);
456 res += count;
457 auto begin_deleted = res;
458 for(int i = 0; i < (current_model_ports - count); i++)
459 {
460 to_delete.push_back(*res);
461 ++res;
462 }
463 m_outlets.erase(begin_deleted, res);
464 }
465
466 dynamic_ports.num_out_ports(idx) = count;
467
468 outletsChanged();
469 for(auto port : to_delete)
470 delete port;
471 }
472
473 void init_all_ports()
474 {
475 InletInitFunc<Info> inlets{*this, m_inlets};
476 OutletInitFunc<Info> outlets{*this, m_outlets};
477 avnd::port_visit_dispatcher<Info>([&inlets]<typename P>(P&& port, auto idx) {
478 if constexpr(!avnd::dynamic_ports_port<P>)
479 inlets(port, idx);
480 }, [&outlets]<typename P>(P&& port, auto idx) {
481 if constexpr(!avnd::dynamic_ports_port<P>)
482 outlets(port, idx);
483 });
484
485 if(!requires { Info::ossia_show_ports_by_default; })
486 if constexpr(oscr::has_ossia_layer<Info>)
487 hideAllInlets(*this);
488 }
489
490public:
491 int expected_input_ports() const noexcept
492 {
493 int count = 0;
494
495 // We have to adjust before accessing a port as there is the first "fake"
496 // port if the processor takes audio by argument
497 if constexpr(avnd::audio_argument_processor<Info>)
498 count += 1;
499 else if constexpr(avnd::tag_cv<Info>)
500 count += 1;
501
502 // The "messages" ports also go before
503 count += avnd::messages_introspection<Info>::size;
504
505 avnd::input_introspection<Info>::for_all([this, &count]<std::size_t Idx, typename P>(
506 avnd::field_reflection<Idx, P> field) {
507 int num_ports = 1;
508 if constexpr(avnd::dynamic_ports_port<P>)
509 num_ports = dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
510 count += num_ports;
511 });
512
513 return count;
514 }
515
516 int expected_output_ports() const noexcept
517 {
518 int count = 0;
519
520 // We have to adjust before accessing a port as there is the first "fake"
521 // port if the processor takes audio by argument
522 if constexpr(avnd::audio_argument_processor<Info>)
523 count += 1;
524 else if constexpr(avnd::tag_cv<Info>)
525 {
526 using operator_ret = typename avnd::function_reflection_o<Info>::return_type;
527 if constexpr(!std::is_void_v<operator_ret>)
528 count += 1;
529 }
530
531 avnd::output_introspection<Info>::for_all(
532 [this,
533 &count]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
534 int num_ports = 1;
535 if constexpr(avnd::dynamic_ports_port<P>)
536 num_ports = dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
537 count += num_ports;
538 });
539
540 return count;
541 }
542
543 std::span<Process::Inlet*> avnd_input_idx_to_model_ports(int index) const noexcept
544 {
545 int model_index = 0;
546
547 // We have to adjust before accessing a port as there is the first "fake"
548 // port if the processor takes audio by argument
549 if constexpr(avnd::audio_argument_processor<Info>)
550 model_index += 1;
551 else if constexpr(avnd::tag_cv<Info>)
552 model_index += 1;
553
554 // The "messages" ports also go before
555 model_index += avnd::messages_introspection<Info>::size;
556
557 std::span<Process::Inlet*> ret;
558 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size == 0)
559 {
560 ret = std::span<Process::Inlet*>(
561 const_cast<Process::Inlet**>(this->m_inlets.data()) + model_index + index, 1);
562 }
563 else
564 {
565 avnd::input_introspection<Info>::for_all(
566 [this, index, &model_index,
567 &ret]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
568 if(Idx == index)
569 {
570 int num_ports = 1;
571 if constexpr(avnd::dynamic_ports_port<P>)
572 {
573 num_ports = dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
574 if(num_ports == 0)
575 {
576 ret = {};
577 return;
578 }
579 }
580 ret = std::span<Process::Inlet*>(
581 const_cast<Process::Inlet**>(this->m_inlets.data()) + model_index,
582 num_ports);
583 }
584 else
585 {
586 if constexpr(avnd::dynamic_ports_port<P>)
587 {
588 model_index += dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
589 }
590 else
591 {
592 model_index += 1;
593 }
594 }
595 });
596 }
597
598 return ret;
599 }
600
601 std::span<Process::Outlet*> avnd_output_idx_to_model_ports(int index) const noexcept
602 {
603 int model_index = 0;
604
605 // We have to adjust before accessing a port as there is the first "fake"
606 // port if the processor takes audio by argument
607 if constexpr(avnd::audio_argument_processor<Info>)
608 model_index += 1;
609 else if constexpr(avnd::tag_cv<Info>)
610 {
611 using operator_ret = typename avnd::function_reflection_o<Info>::return_type;
612 if constexpr(!std::is_void_v<operator_ret>)
613 model_index += 1;
614 }
615
616 // The "messages" ports also go before
617 model_index += avnd::messages_introspection<Info>::size;
618
619 std::span<Process::Outlet*> ret;
620 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size == 0)
621 {
622 ret = std::span<Process::Outlet*>(
623 const_cast<Process::Outlet**>(this->m_outlets.data()) + model_index + index,
624 1);
625 }
626 else
627 {
628 avnd::output_introspection<Info>::for_all(
629 [this, index, &model_index,
630 &ret]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
631 if(Idx == index)
632 {
633 int num_ports = 1;
634 if constexpr(avnd::dynamic_ports_port<P>)
635 {
636 num_ports = dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
637 if(num_ports == 0)
638 {
639 ret = {};
640 return;
641 }
642 }
643 ret = std::span<Process::Outlet*>(
644 const_cast<Process::Outlet**>(this->m_outlets.data()) + model_index,
645 num_ports);
646 }
647 else
648 {
649 if constexpr(avnd::dynamic_ports_port<P>)
650 {
651 model_index += dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
652 }
653 else
654 {
655 model_index += 1;
656 }
657 }
658 });
659 }
660
661 return ret;
662 }
663
664 Process::Inlets::iterator avnd_input_idx_to_iterator(int index) const noexcept
665 {
666 int model_index = 0;
667
668 // We have to adjust before accessing a port as there is the first "fake"
669 // port if the processor takes audio by argument
670 if constexpr(avnd::audio_argument_processor<Info>)
671 model_index += 1;
672
673 // The "messages" ports also go before
674 model_index += avnd::messages_introspection<Info>::size;
675
676 Process::Inlets::iterator ret;
677 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size == 0)
678 {
679 ret = const_cast<ProcessModel*>(this)->m_inlets.begin() + model_index;
680 }
681 else
682 {
683 avnd::input_introspection<Info>::for_all(
684 [this, index, &model_index,
685 &ret]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
686 if(Idx == index)
687 {
688 ret = const_cast<ProcessModel*>(this)->m_inlets.begin() + model_index;
689 }
690 else
691 {
692 if constexpr(avnd::dynamic_ports_port<P>)
693 {
694 model_index += dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
695 }
696 else
697 {
698 model_index += 1;
699 }
700 }
701 });
702 }
703 return ret;
704 }
705
706 Process::Outlets::iterator avnd_output_idx_to_iterator(int index) const noexcept
707 {
708 int model_index = 0;
709
710 // We have to adjust before accessing a port as there is the first "fake"
711 // port if the processor takes audio by argument
712 if constexpr(avnd::audio_argument_processor<Info>)
713 model_index += 1;
714
715 // The "messages" ports also go before
716 model_index += avnd::messages_introspection<Info>::size;
717
718 Process::Outlets::iterator ret;
719 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size == 0)
720 {
721 ret = const_cast<ProcessModel*>(this)->m_outlets.begin() + model_index;
722 }
723 else
724 {
725 avnd::output_introspection<Info>::for_all(
726 [this, index, &model_index,
727 &ret]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
728 if(Idx == index)
729 {
730 ret = const_cast<ProcessModel*>(this)->m_outlets.begin() + model_index;
731 }
732 else
733 {
734 if constexpr(avnd::dynamic_ports_port<P>)
735 {
736 model_index += dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
737 }
738 else
739 {
740 model_index += 1;
741 }
742 }
743 });
744 }
745 return ret;
746 }
747};
748}
749
750template <typename Info>
751struct is_custom_serialized<oscr::ProcessModel<Info>> : std::true_type
752{
753};
754
755template <typename Info>
756struct TSerializer<DataStream, oscr::ProcessModel<Info>>
757{
759 static void readFrom(DataStream::Serializer& s, const model_type& obj)
760 {
761 Process::readPorts(s, obj.m_inlets, obj.m_outlets);
762
763 // Save the recorded amount of dynamic ports for each port
764 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
765 {
766 avnd::dynamic_ports_input_introspection<Info>::for_all(
767 [&obj, &s]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
768 if constexpr(avnd::dynamic_ports_port<P>)
769 s.stream() << obj.dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
770 });
771 }
772 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
773 {
774 avnd::dynamic_ports_output_introspection<Info>::for_all(
775 [&obj, &s]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
776 if constexpr(avnd::dynamic_ports_port<P>)
777 s.stream() << obj.dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
778 });
779 }
780
781 s.insertDelimiter();
782 }
783
784 static void writeTo(DataStream::Deserializer& s, model_type& obj)
785 {
786 Process::writePorts(
787 s, s.components.interfaces<Process::PortFactoryList>(), obj.m_inlets,
788 obj.m_outlets, &obj);
789
790 // Read the recorded amount of dynamic ports for each port
791 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
792 {
793 avnd::dynamic_ports_input_introspection<Info>::for_all(
794 [&obj, &s]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
795 if constexpr(avnd::dynamic_ports_port<P>)
796 s.stream() >> obj.dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
797 });
798 }
799 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
800 {
801 avnd::dynamic_ports_output_introspection<Info>::for_all(
802 [&obj, &s]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
803 if constexpr(avnd::dynamic_ports_port<P>)
804 s.stream() >> obj.dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
805 });
806 }
807 s.checkDelimiter();
808 }
809};
810
811template <typename Info>
812struct TSerializer<JSONObject, oscr::ProcessModel<Info>>
813{
815 static void readFrom(JSONObject::Serializer& s, const model_type& obj)
816 {
817 Process::readPorts(s, obj.m_inlets, obj.m_outlets);
818 // Save the recorded amount of dynamic ports for each port
819 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
820 {
821 ossia::string_map<int> indices;
822 avnd::dynamic_ports_input_introspection<Info>::for_all(
823 [&obj,
824 &indices]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
825 if constexpr(avnd::dynamic_ports_port<P>)
826 {
827 indices[std::string(avnd::get_c_identifier<P>())]
828 = obj.dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
829 }
830 });
831 s.obj["DynamicInlets"] = indices;
832 }
833 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
834 {
835 ossia::string_map<int> indices;
836 avnd::dynamic_ports_output_introspection<Info>::for_all(
837 [&obj,
838 &indices]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
839 if constexpr(avnd::dynamic_ports_port<P>)
840 indices[std::string(avnd::get_c_identifier<P>())]
841 = obj.dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
842 });
843 s.obj["DynamicOutlets"] = indices;
844 }
845 }
846
847 static void writeTo(JSONObject::Deserializer& s, model_type& obj)
848 {
849 Process::writePorts(
850 s, s.components.interfaces<Process::PortFactoryList>(), obj.m_inlets,
851 obj.m_outlets, &obj);
852 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
853 {
854 if(auto val = s.obj.tryGet("DynamicInlets"))
855 {
856 static ossia::string_map<int> indices;
857 indices.clear();
858 indices <<= *val;
859 avnd::dynamic_ports_input_introspection<Info>::for_all(
860 [&obj]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
861 if constexpr(avnd::dynamic_ports_port<P>)
862 obj.dynamic_ports.num_in_ports(avnd::field_index<Idx>{})
863 = indices[std::string(avnd::get_c_identifier<P>())];
864 });
865 }
866 }
867 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
868 {
869 if(auto val = s.obj.tryGet("DynamicOutlets"))
870 {
871 static ossia::string_map<int> indices;
872 indices.clear();
873 indices <<= *val;
874 avnd::dynamic_ports_output_introspection<Info>::for_all(
875 [&obj]<std::size_t Idx, typename P>(avnd::field_reflection<Idx, P> field) {
876 if constexpr(avnd::dynamic_ports_port<P>)
877 obj.dynamic_ports.num_out_ports(avnd::field_index<Idx>{})
878 = indices[std::string(avnd::get_c_identifier<P>())];
879 });
880 }
881 }
882 }
883};
Definition VisitorInterface.hpp:53
Definition DataStreamVisitor.hpp:27
void insertDelimiter()
insertDelimiter
Definition DataStreamVisitor.hpp:156
Definition DataStreamVisitor.hpp:202
Definition VisitorInterface.hpp:61
Definition JSONVisitor.hpp:52
Definition JSONVisitor.hpp:423
Definition Port.hpp:178
Definition Port.hpp:274
Definition PortFactory.hpp:74
The Process class.
Definition score-lib-process/Process/Process.hpp:61
The id_base_t class.
Definition Identifier.hpp:59
Definition score-plugin-avnd/Crousti/ProcessModel.hpp:86
Definition Metadatas.hpp:34
Definition Factories.hpp:19
Definition CableHelpers.hpp:74
Static metadata implementation.
Definition lib/score/tools/Metadata.hpp:36
Definition PortForward.hpp:23
Definition PortForward.hpp:27
Definition VisitorInterface.hpp:13
Definition TimeValue.hpp:21
Definition score-plugin-avnd/Crousti/ProcessModel.hpp:50
Definition score-plugin-avnd/Crousti/ProcessModel.hpp:45