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