2 #include <Process/GenericProcessFactory.hpp>
3 #include <Process/Process.hpp>
4 #include <Process/ProcessFactory.hpp>
6 #include <Crousti/Attributes.hpp>
7 #include <Crousti/Concepts.hpp>
8 #include <Crousti/MessageBus.hpp>
9 #include <Crousti/Metadata.hpp>
10 #include <Crousti/Metadatas.hpp>
11 #include <Crousti/ProcessModelPortInit.hpp>
12 #include <Dataflow/Commands/CableHelpers.hpp>
14 #include <score/serialization/MapSerialization.hpp>
15 #include <score/tools/std/HashMap.hpp>
17 #include <ossia/detail/string_map.hpp>
18 #include <ossia/detail/type_if.hpp>
19 #include <ossia/detail/typelist.hpp>
21 #include <boost/pfr.hpp>
25 #include <avnd/binding/ossia/data_node.hpp>
26 #include <avnd/binding/ossia/dynamic_ports.hpp>
27 #include <avnd/common/for_nth.hpp>
28 #include <avnd/concepts/gfx.hpp>
29 #include <avnd/concepts/ui.hpp>
30 #include <avnd/introspection/messages.hpp>
31 #include <avnd/wrappers/bus_host_process_adapter.hpp>
33 #include <score_plugin_engine.hpp>
42 template <
typename Info>
47 template <
typename Info>
54 struct dummy_ui_callback
56 void operator()(
const QByteArray& arr) noexcept { }
60 template <avnd::has_processor_to_gui_bus Info>
63 std::function<void(QByteArray)> to_ui = dummy_ui_callback{};
66 template <avnd::has_gui_to_processor_bus Info>
69 std::function<void(QByteArray)> from_ui = dummy_ui_callback{};
72 template <
typename Info>
78 SCORE_SERIALIZE_FRIENDS
81 friend struct
TSerializer<JSONObject, oscr::ProcessModel<Info>>;
85 oscr::dynamic_ports_storage<Info> dynamic_ports;
88 ossia::type_if<Info, oscr::has_dynamic_ports<Info>> object_storage_for_ports_callbacks;
91 const TimeVal& duration, const Id<Process::ProcessModel>& id,
92 const score::DocumentContext& ctx, QObject* parent)
96 metadata().setInstanceName(*
this);
99 init_before_port_creation();
101 init_after_port_creation();
105 const TimeVal& duration,
const QString& custom,
110 metadata().setInstanceName(*
this);
113 init_before_port_creation();
116 if constexpr(avnd::file_input_introspection<Info>::size > 0)
118 static constexpr
auto idx
119 = avnd::file_input_introspection<Info>::index_to_field_index(0);
120 setupInitialStringPort(idx, custom);
122 else if constexpr(avnd::control_input_introspection<Info>::size > 0)
124 static constexpr
auto idx
125 = avnd::control_input_introspection<Info>::index_to_field_index(0);
127 typename avnd::control_input_introspection<Info>::template nth_element<0>;
128 if constexpr(avnd::string_ish<decltype(type::value)>)
129 setupInitialStringPort(idx, custom);
131 init_after_port_creation();
134 void setupInitialStringPort(
int idx,
const QString& custom) noexcept
137 auto pp = safe_cast<Process::ControlInlet*>(port);
139 if(pp->value().target<std::string>())
141 pp->setValue(custom.toStdString());
145 template <
typename Impl>
150 init_before_port_creation();
153 init_after_port_creation();
161 if constexpr(avnd::tag_loops_by_default<Info>)
165 void init_before_port_creation() { init_dynamic_ports(); }
166 void init_after_port_creation() { init_controller_ports(); }
167 void check_all_ports()
169 if(std::ssize(m_inlets) != expected_input_ports()
170 || std::ssize(m_outlets) != expected_output_ports())
172 qDebug() <<
"Warning : process does not match spec: I " << m_inlets.size()
173 <<
"but expected: " << expected_input_ports() <<
" ; O "
174 << m_outlets.size() <<
"but expected: " << expected_output_ports();
176 std::vector<Dataflow::SavedPort> m_oldInlets, m_oldOutlets;
177 for(
auto& port : m_inlets)
178 m_oldInlets.emplace_back(
180 for(
auto& port : m_outlets)
181 m_oldOutlets.emplace_back(
184 qDeleteAll(m_inlets);
186 qDeleteAll(m_outlets);
191 Dataflow::reloadPortsInNewProcess(m_oldInlets, m_oldOutlets, *
this);
195 void init_controller_ports()
198 avnd::dynamic_ports_input_introspection<Info>::size > 0
199 || avnd::dynamic_ports_output_introspection<Info>::size > 0)
201 avnd::control_input_introspection<Info>::for_all_n2(
202 avnd::get_inputs<Info>((Info&)this->object_storage_for_ports_callbacks),
203 [
this]<std::size_t Idx,
typename F>(
204 F& field,
auto pred_index, avnd::field_index<Idx>) {
205 Info& obj = this->object_storage_for_ports_callbacks;
206 if constexpr(requires { F::on_controller_setup(); })
208 auto controller_inlets = avnd_input_idx_to_model_ports(Idx);
209 SCORE_ASSERT(controller_inlets.size() == 1);
210 auto inlet = qobject_cast<Process::ControlInlet*>(controller_inlets[0]);
212 oscr::from_ossia_value(inlet->value(), field.value);
214 if_possible(field.update(obj));
216 F::on_controller_setup()(obj, field.value);
218 if constexpr(requires { F::on_controller_interaction(); })
220 auto controller_inlets = avnd_input_idx_to_model_ports(Idx);
221 SCORE_ASSERT(controller_inlets.size() == 1);
222 auto inlet = qobject_cast<Process::ControlInlet*>(controller_inlets[0]);
223 inlet->noValueChangeOnMove =
true;
225 inlet, &Process::ControlInlet::valueChanged,
226 [
this, &field](
const ossia::value& val) {
227 Info& obj = this->object_storage_for_ports_callbacks;
228 oscr::from_ossia_value(val, field.value);
230 if_possible(field.update(obj));
232 F::on_controller_interaction()(obj, field.value);
237 if constexpr(avnd::has_gui_to_processor_bus<Info>)
242 if constexpr(avnd::has_processor_to_gui_bus<Info>)
244 Info& obj = this->object_storage_for_ports_callbacks;
246 obj.send_message = [
this]<
typename T>(T&& b)
mutable {
254 void init_dynamic_ports()
290 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
292 Info& obj = object_storage_for_ports_callbacks;
293 avnd::dynamic_ports_input_introspection<Info>::for_all_n2(
294 avnd::get_inputs(obj),
295 [&]<std::size_t N>(
auto& port,
auto pred_idx, avnd::field_index<N> field_idx) {
296 port.request_port_resize = [
this, &port](
int new_count) {
300 QTimer::singleShot(0,
this, [
self = QPointer{
this}, &port, new_count] {
302 self->request_new_dynamic_input_count(
303 port, avnd::field_index<N>{}, new_count);
309 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
311 Info& obj = object_storage_for_ports_callbacks;
312 avnd::dynamic_ports_output_introspection<Info>::for_all_n2(
313 avnd::get_outputs(obj),
314 [&]<std::size_t N>(
auto& port,
auto pred_idx, avnd::field_index<N> field_idx) {
315 port.request_port_resize = [
this, &port](
int new_count) {
319 QTimer::singleShot(0,
this, [
self = QPointer{
this}, &port, new_count] {
321 self->request_new_dynamic_output_count(
322 port, avnd::field_index<N>{}, new_count);
329 template <
typename P, std::
size_t N>
330 void request_new_dynamic_input_count(P& port, avnd::field_index<N> idx,
int count)
332 const int current_model_ports = dynamic_ports.num_in_ports(idx);
333 if(current_model_ports == count || count < 0 || count > 512)
336 ossia::small_pod_vector<Process::Inlet*, 4> to_delete;
337 if(current_model_ports < count)
341 auto res = avnd_input_idx_to_iterator(idx);
342 res += current_model_ports;
345 inlets.inlet = 10000 + N * 1000 + current_model_ports;
347 for(
int i = current_model_ports; i < count; i++)
350 if(std::ssize(inlets_to_add) > sz)
352 sz = std::ssize(inlets_to_add);
353 auto new_inlet = inlets_to_add.back();
354 if(
auto nm = new_inlet->name(); nm.contains(
"{}"))
356 nm.replace(
"{}", QString::number(i));
357 new_inlet->setName(nm);
361 m_inlets.insert(res, inlets_to_add.begin(), inlets_to_add.end());
363 else if(current_model_ports > count)
366 auto res = avnd_input_idx_to_iterator(idx);
368 auto begin_deleted = res;
369 for(
int i = 0; i < (current_model_ports - count); i++)
371 to_delete.push_back(*res);
374 m_inlets.erase(begin_deleted, res);
377 dynamic_ports.num_in_ports(idx) = count;
380 for(
auto port : to_delete)
384 template <
typename P, std::
size_t N>
385 void request_new_dynamic_output_count(P& port, avnd::field_index<N> idx,
int count)
387 const int current_model_ports = dynamic_ports.num_out_ports(idx);
388 if(current_model_ports == count || count < 0 || count > 512)
391 ossia::small_pod_vector<Process::Outlet*, 4> to_delete;
392 if(current_model_ports < count)
396 auto res = avnd_output_idx_to_iterator(idx);
397 res += current_model_ports;
400 outlets.outlet = 1000000 + N * 1000 + current_model_ports;
402 for(
int i = current_model_ports; i < count; i++)
405 if(std::ssize(outlets_to_add) > sz)
407 sz = std::ssize(outlets_to_add);
408 auto new_outlet = outlets_to_add.back();
409 if(
auto nm = new_outlet->name(); nm.contains(
"{}"))
411 nm.replace(
"{}", QString::number(i));
412 new_outlet->setName(nm);
416 m_outlets.insert(res, outlets_to_add.begin(), outlets_to_add.end());
418 else if(current_model_ports > count)
421 auto res = avnd_output_idx_to_iterator(idx);
423 auto begin_deleted = res;
424 for(
int i = 0; i < (current_model_ports - count); i++)
426 to_delete.push_back(*res);
429 m_outlets.erase(begin_deleted, res);
432 dynamic_ports.num_out_ports(idx) = count;
435 for(
auto port : to_delete)
439 void init_all_ports()
443 avnd::port_visit_dispatcher<Info>([&inlets]<
typename P>(P&& port,
auto idx) {
444 if constexpr(!avnd::dynamic_ports_port<P>)
446 }, [&outlets]<
typename P>(P&& port,
auto idx) {
447 if constexpr(!avnd::dynamic_ports_port<P>)
453 int expected_input_ports()
const noexcept
459 if constexpr(avnd::audio_argument_processor<Info>)
461 else if constexpr(avnd::tag_cv<Info>)
465 count += avnd::messages_introspection<Info>::size;
467 avnd::input_introspection<Info>::for_all([
this, &count]<std::size_t Idx,
typename P>(
468 avnd::field_reflection<Idx, P> field) {
470 if constexpr(avnd::dynamic_ports_port<P>)
471 num_ports = dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
478 int expected_output_ports()
const noexcept
484 if constexpr(avnd::audio_argument_processor<Info>)
486 else if constexpr(avnd::tag_cv<Info>)
488 using operator_ret =
typename avnd::function_reflection_o<Info>::return_type;
489 if constexpr(!std::is_void_v<operator_ret>)
493 avnd::output_introspection<Info>::for_all(
495 &count]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
497 if constexpr(avnd::dynamic_ports_port<P>)
498 num_ports = dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
505 std::span<Process::Inlet*> avnd_input_idx_to_model_ports(
int index)
const noexcept
511 if constexpr(avnd::audio_argument_processor<Info>)
513 else if constexpr(avnd::tag_cv<Info>)
517 model_index += avnd::messages_introspection<Info>::size;
519 std::span<Process::Inlet*> ret;
520 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size == 0)
522 ret = std::span<Process::Inlet*>(
523 const_cast<Process::Inlet**
>(this->m_inlets.data()) + model_index + index, 1);
527 avnd::input_introspection<Info>::for_all(
528 [
this, index, &model_index,
529 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
533 if constexpr(avnd::dynamic_ports_port<P>)
535 num_ports = dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
542 ret = std::span<Process::Inlet*>(
543 const_cast<Process::Inlet**
>(this->m_inlets.data()) + model_index,
548 if constexpr(avnd::dynamic_ports_port<P>)
550 model_index += dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
563 std::span<Process::Outlet*> avnd_output_idx_to_model_ports(
int index)
const noexcept
569 if constexpr(avnd::audio_argument_processor<Info>)
571 else if constexpr(avnd::tag_cv<Info>)
573 using operator_ret =
typename avnd::function_reflection_o<Info>::return_type;
574 if constexpr(!std::is_void_v<operator_ret>)
579 model_index += avnd::messages_introspection<Info>::size;
581 std::span<Process::Outlet*> ret;
582 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size == 0)
584 ret = std::span<Process::Outlet*>(
585 const_cast<Process::Outlet**
>(this->m_outlets.data()) + model_index + index,
590 avnd::output_introspection<Info>::for_all(
591 [
this, index, &model_index,
592 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
596 if constexpr(avnd::dynamic_ports_port<P>)
598 num_ports = dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
605 ret = std::span<Process::Outlet*>(
611 if constexpr(avnd::dynamic_ports_port<P>)
613 model_index += dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
626 Process::Inlets::iterator avnd_input_idx_to_iterator(
int index)
const noexcept
632 if constexpr(avnd::audio_argument_processor<Info>)
636 model_index += avnd::messages_introspection<Info>::size;
638 Process::Inlets::iterator ret;
639 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size == 0)
641 ret =
const_cast<ProcessModel*
>(
this)->m_inlets.begin() + model_index;
645 avnd::input_introspection<Info>::for_all(
646 [
this, index, &model_index,
647 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
650 ret =
const_cast<ProcessModel*
>(
this)->m_inlets.begin() + model_index;
654 if constexpr(avnd::dynamic_ports_port<P>)
656 model_index += dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
668 Process::Outlets::iterator avnd_output_idx_to_iterator(
int index)
const noexcept
674 if constexpr(avnd::audio_argument_processor<Info>)
678 model_index += avnd::messages_introspection<Info>::size;
680 Process::Outlets::iterator ret;
681 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size == 0)
683 ret =
const_cast<ProcessModel*
>(
this)->m_outlets.begin() + model_index;
687 avnd::output_introspection<Info>::for_all(
688 [
this, index, &model_index,
689 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
692 ret =
const_cast<ProcessModel*
>(
this)->m_outlets.begin() + model_index;
696 if constexpr(avnd::dynamic_ports_port<P>)
698 model_index += dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
712 template <
typename Info>
713 struct is_custom_serialized<
oscr::ProcessModel<Info>> : std::true_type
717 template <
typename Info>
723 Process::readPorts(s, obj.m_inlets, obj.m_outlets);
726 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
728 avnd::dynamic_ports_input_introspection<Info>::for_all(
729 [&obj, &s]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
730 if constexpr(avnd::dynamic_ports_port<P>)
731 s.stream() << obj.dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
734 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
736 avnd::dynamic_ports_output_introspection<Info>::for_all(
737 [&obj, &s]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
738 if constexpr(avnd::dynamic_ports_port<P>)
739 s.stream() << obj.dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
750 obj.m_outlets, &obj);
753 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
755 avnd::dynamic_ports_input_introspection<Info>::for_all(
756 [&obj, &s]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
757 if constexpr(avnd::dynamic_ports_port<P>)
758 s.stream() >> obj.dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
761 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
763 avnd::dynamic_ports_output_introspection<Info>::for_all(
764 [&obj, &s]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
765 if constexpr(avnd::dynamic_ports_port<P>)
766 s.stream() >> obj.dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
773 template <
typename Info>
779 Process::readPorts(s, obj.m_inlets, obj.m_outlets);
781 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
783 ossia::string_map<int> indices;
784 avnd::dynamic_ports_input_introspection<Info>::for_all(
786 &indices]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
787 if constexpr(avnd::dynamic_ports_port<P>)
789 indices[std::string(avnd::get_c_identifier<P>())]
790 = obj.dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
793 s.obj[
"DynamicInlets"] = indices;
795 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
797 ossia::string_map<int> indices;
798 avnd::dynamic_ports_output_introspection<Info>::for_all(
800 &indices]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
801 if constexpr(avnd::dynamic_ports_port<P>)
802 indices[std::string(avnd::get_c_identifier<P>())]
803 = obj.dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
805 s.obj[
"DynamicOutlets"] = indices;
813 obj.m_outlets, &obj);
814 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
816 if(
auto val = s.obj.tryGet(
"DynamicInlets"))
818 ossia::string_map<int> indices;
820 avnd::dynamic_ports_input_introspection<Info>::for_all(
821 [&obj, &indices]<std::size_t Idx,
typename P>(
822 avnd::field_reflection<Idx, P> field) {
823 if constexpr(avnd::dynamic_ports_port<P>)
824 obj.dynamic_ports.num_in_ports(avnd::field_index<Idx>{})
825 = indices[std::string(avnd::get_c_identifier<P>())];
829 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
831 if(
auto val = s.obj.tryGet(
"DynamicOutlets"))
833 ossia::string_map<int> indices;
835 avnd::dynamic_ports_output_introspection<Info>::for_all(
836 [&obj, &indices]<std::size_t Idx,
typename P>(
837 avnd::field_reflection<Idx, P> field) {
838 if constexpr(avnd::dynamic_ports_port<P>)
839 obj.dynamic_ports.num_out_ports(avnd::field_index<Idx>{})
840 = indices[std::string(avnd::get_c_identifier<P>())];
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: PortFactory.hpp:74
The Process class.
Definition: score-lib-process/Process/Process.hpp:61
The id_base_t class.
Definition: Identifier.hpp:57
Definition: score-plugin-avnd/Crousti/ProcessModel.hpp:77
Definition: Factories.hpp:19
Definition: CableHelpers.hpp:66
Definition: PortForward.hpp:23
Definition: PortForward.hpp:27
Definition: VisitorInterface.hpp:13
Definition: TimeValue.hpp:21
Definition: ProcessModelPortInit.hpp:55
Definition: MessageBus.hpp:37
Definition: score-plugin-avnd/Crousti/ProcessModel.hpp:49
Definition: score-plugin-avnd/Crousti/ProcessModel.hpp:44
Definition: ProcessModelPortInit.hpp:277