89 SCORE_SERIALIZE_FRIENDS
92 friend struct
TSerializer<JSONObject, oscr::ProcessModel<Info>>;
96 oscr::dynamic_ports_storage<Info> dynamic_ports;
99 ossia::type_if<Info, oscr::has_ports_callbacks<Info>>
100 object_storage_for_ports_callbacks;
103 const TimeVal& duration, const Id<Process::ProcessModel>& id,
104 const score::DocumentContext& ctx, QObject* parent)
108 metadata().setInstanceName(*
this);
111 init_before_port_creation();
113 init_after_port_creation();
117 const TimeVal& duration,
const QString& custom,
122 metadata().setInstanceName(*
this);
125 init_before_port_creation();
128 if constexpr(avnd::file_input_introspection<Info>::size > 0)
130 static constexpr auto idx
131 = avnd::file_input_introspection<Info>::index_to_field_index(0);
132 setupInitialStringPort(idx, custom);
134 else if constexpr(avnd::control_input_introspection<Info>::size > 0)
136 static constexpr auto idx
137 = avnd::control_input_introspection<Info>::index_to_field_index(0);
139 typename avnd::control_input_introspection<Info>::template nth_element<0>;
140 if constexpr(avnd::string_ish<
decltype(type::value)>)
141 setupInitialStringPort(idx, custom);
143 init_after_port_creation();
146 void setupInitialStringPort(
int idx,
const QString& custom)
noexcept
149 auto pp = safe_cast<Process::ControlInlet*>(port);
151 if(
auto val = pp->value(); bool(val.target<std::string>()))
153 pp->setValue(custom.toStdString());
157 template <
typename Impl>
162 init_before_port_creation();
165 init_after_port_creation();
173 if constexpr(avnd::tag_loops_by_default<Info>)
177 void init_before_port_creation() { init_dynamic_ports(); }
178 void init_after_port_creation()
180 init_controller_ports();
181 init_folder_comboboxes();
188 void init_folder_comboboxes()
190 for(
auto* inl : m_inlets)
192 auto combo = qobject_cast<Process::ComboBox*>(inl);
193 if(!combo || combo->folderPortName.isEmpty())
197 for(
auto* other : m_inlets)
199 auto ci = qobject_cast<Process::ControlInlet*>(other);
200 if(ci && ci != combo && ci->name() == combo->folderPortName)
210 if(
auto s = p->value().target<std::string>())
215 auto path = QString::fromStdString(*s);
222 for(QObject* o = this->parent(); o && !doc; o = o->parent())
223 doc = qobject_cast<score::Document*>(o);
228 if(QFileInfo fi{path}; fi.isFile())
229 return fi.absolutePath();
235 combo->repopulateFromFolder(pathOf(folderPort));
238 folderPort, &Process::ControlInlet::valueChanged, combo,
239 [combo, folderPort, pathOf](
const ossia::value&) {
240 combo->repopulateFromFolder(pathOf(folderPort));
244 void check_all_ports()
246 if(std::ssize(m_inlets) != expected_input_ports()
247 || std::ssize(m_outlets) != expected_output_ports())
249 qDebug() <<
typeid(Info).name() << this->metadata().getName()
250 <<
": WARNING : process does not match spec: I " << m_inlets.size()
251 <<
"but expected: " << expected_input_ports() <<
" ; O "
252 << m_outlets.size() <<
"but expected: " << expected_output_ports();
254 std::vector<Dataflow::SavedPort> m_oldInlets, m_oldOutlets;
255 for(
auto& port : m_inlets)
256 m_oldInlets.emplace_back(
258 for(
auto& port : m_outlets)
259 m_oldOutlets.emplace_back(
262 qDeleteAll(m_inlets);
264 qDeleteAll(m_outlets);
269 Dataflow::reloadPortsInNewProcess(m_oldInlets, m_oldOutlets, *
this);
280 template <
typename F, std::
size_t Idx>
283 auto ports = avnd_input_idx_to_model_ports(Idx);
284 SCORE_ASSERT(ports.size() == 1);
285 auto old_inlet = qobject_cast<Process::ControlInlet*>(ports[0]);
286 SCORE_ASSERT(old_inlet);
289 InletInitFunc<Info> make{*
this, fresh};
290 make.inlet = old_inlet->id().val();
292 if(fresh.size() != 1)
298 auto new_inlet = qobject_cast<Process::ControlInlet*>(fresh[0]);
299 if(!new_inlet || new_inlet->concreteKey() == old_inlet->concreteKey())
305 new_inlet->loadData(old_inlet->saveData(), Process::PortLoadDataFlags::ReloadValue);
306 new_inlet->setExposed(old_inlet->exposed());
307 new_inlet->setDescription(old_inlet->description());
308 new_inlet->displayHandledExplicitly = old_inlet->displayHandledExplicitly;
310 auto it = ossia::find(m_inlets, old_inlet);
311 SCORE_ASSERT(it != m_inlets.end());
317 void init_controller_ports()
321 avnd::control_input_introspection<Info>::for_all_n2(
322 avnd::get_inputs<Info>((Info&)this->object_storage_for_ports_callbacks),
323 [
this]<std::size_t Idx,
typename F>(
324 F& field,
auto pred_index, avnd::field_index<Idx> idx) {
325 Info& obj = this->object_storage_for_ports_callbacks;
326 if constexpr(
requires { F::on_controller_interaction(); })
328 upgradeControllerPort(field, idx);
331 if constexpr(
requires(ossia::value v) { F::migrate_value(v); })
333 auto inlet = qobject_cast<Process::ControlInlet*>(
334 avnd_input_idx_to_model_ports(Idx)[0]);
335 inlet->setValue(F::migrate_value(inlet->value()));
338 if constexpr(
requires { F::on_controller_setup(); })
340 auto controller_inlets = avnd_input_idx_to_model_ports(Idx);
341 SCORE_ASSERT(controller_inlets.size() == 1);
342 auto inlet = qobject_cast<Process::ControlInlet*>(controller_inlets[0]);
344 oscr::from_ossia_value(inlet->value(), field.value);
346 if_possible(field.update(obj));
348 F::on_controller_setup()(obj, field.value);
350 if constexpr(
requires { F::on_controller_interaction(); })
352 auto controller_inlets = avnd_input_idx_to_model_ports(Idx);
353 SCORE_ASSERT(controller_inlets.size() == 1);
354 auto inlet = qobject_cast<Process::ControlInlet*>(controller_inlets[0]);
355 inlet->noValueChangeOnMove =
true;
357 if constexpr(!
requires { F::on_controller_setup(); })
359 oscr::from_ossia_value(inlet->value(), field.value);
360 if_possible(field.update(obj));
361 F::on_controller_interaction()(obj, field.value);
365 inlet, &Process::ControlInlet::valueChanged,
366 [
this, &field](
const ossia::value& val) {
367 Info& obj = this->object_storage_for_ports_callbacks;
368 oscr::from_ossia_value(val, field.value);
370 if_possible(field.update(obj));
372 F::on_controller_interaction()(obj, field.value);
377 if constexpr(avnd::has_gui_to_processor_bus<Info>)
382 if constexpr(avnd::has_processor_to_gui_bus<Info>)
384 Info& obj = this->object_storage_for_ports_callbacks;
386 obj.send_message = [
this]<
typename T>(T&& b)
mutable {
388 MessageBusSender{this->to_ui}(std::move(b));
394 void init_dynamic_ports()
430 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
432 Info& obj = object_storage_for_ports_callbacks;
433 avnd::dynamic_ports_input_introspection<Info>::for_all_n2(
434 avnd::get_inputs(obj),
435 [&]<std::size_t N>(
auto& port,
auto pred_idx, avnd::field_index<N> field_idx) {
436 port.request_port_resize = [
this, &port](
int new_count) {
437 this->request_new_dynamic_input_count(port, avnd::field_index<N>{}, new_count);
439 if constexpr(
requires { port.request_port_rows; })
440 port.request_port_rows = [
this, &port](
const auto& rows) {
441 this->
template request_dynamic_rows<true>(
442 port, avnd::field_index<N>{}, rows);
447 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
449 Info& obj = object_storage_for_ports_callbacks;
450 avnd::dynamic_ports_output_introspection<Info>::for_all_n2(
451 avnd::get_outputs(obj),
452 [&]<std::size_t N>(
auto& port,
auto pred_idx, avnd::field_index<N> field_idx) {
453 port.request_port_resize = [
this, &port](
int new_count) {
454 this->request_new_dynamic_output_count(
455 port, avnd::field_index<N>{}, new_count);
457 if constexpr(
requires { port.request_port_rows; })
458 port.request_port_rows = [
this, &port](
const auto& rows) {
459 this->
template request_dynamic_rows<false>(
460 port, avnd::field_index<N>{}, rows);
466 template <
bool Input,
typename P, std::
size_t N,
typename Rows>
467 void request_dynamic_rows(P& port, avnd::field_index<N> idx,
const Rows& rows)
469 if(rows.size() > 512)
471 std::vector<int> keys;
472 keys.reserve(rows.size());
473 for(
const auto& [key, text] : rows)
475 if(key < 10000 || ossia::contains(keys, key))
479 auto& all = [&]() ->
auto& {
487 return avnd_input_idx_to_iterator(idx);
489 return avnd_output_idx_to_iterator(idx);
491 auto& count = [&]() ->
auto& {
493 return dynamic_ports.num_in_ports(idx);
495 return dynamic_ports.num_out_ports(idx);
497 using Ports = std::decay_t<
decltype(all)>;
498 using Port = std::remove_pointer_t<typename Ports::value_type>;
499 Ports old{first, first + count}, next;
500 next.reserve(rows.size());
501 for(
const auto& [key, text] : rows)
504 = ossia::find_if(old, [key](
auto p) {
return p && p->id().val() == key; });
506 if(found != old.end())
516 InletInitFunc<Info> make{*
this, added};
522 OutletInitFunc<Info> make{*
this, added};
526 SCORE_ASSERT(added.size() == 1);
529 p->stableIdentity =
true;
530 p->setName(QString::fromStdString(text));
533 const bool changed = !std::equal(next.begin(), next.end(), first, first + count);
534 const auto offset = first - all.begin();
535 all.erase(first, first + count);
536 all.insert(all.begin() + offset, next.begin(), next.end());
538 ossia::small_pod_vector<Port*, 4> removed;
541 removed.push_back(p);
542 removeCablesOfPorts(removed);
550 for(
auto p : removed)
558 template <
typename Port_T>
559 void removeCablesOfPorts(
const ossia::small_pod_vector<Port_T*, 4>& ports)
567 for(QObject* o = this->parent(); o && !doc; o = o->parent())
568 doc = qobject_cast<score::Document*>(o);
573 objs.reserve(ports.size());
577 const auto& ctx = doc->context();
578 if(
auto cables = Dataflow::saveCables(objs, ctx); !cables.empty())
579 Dataflow::removeCables(cables, ctx);
582 template <
typename P, std::
size_t N>
583 void request_new_dynamic_input_count(P& port, avnd::field_index<N> idx,
int count)
585 const int current_model_ports = dynamic_ports.num_in_ports(idx);
586 if(current_model_ports == count || count < 0 || count > 512)
589 ossia::small_pod_vector<Process::Inlet*, 4> to_delete;
590 if(current_model_ports < count)
594 auto res = avnd_input_idx_to_iterator(idx);
595 res += current_model_ports;
597 InletInitFunc<Info> inlets{*
this, inlets_to_add};
598 inlets.inlet = 10000 + N * 1000 + current_model_ports;
600 for(
int i = current_model_ports; i < count; i++)
603 if(std::ssize(inlets_to_add) > sz)
605 sz = std::ssize(inlets_to_add);
606 auto new_inlet = inlets_to_add.back();
607 if(
auto nm = new_inlet->name(); nm.contains(
"{}"))
609 nm.replace(
"{}", QString::number(i));
610 new_inlet->setName(nm);
614 m_inlets.insert(res, inlets_to_add.begin(), inlets_to_add.end());
616 else if(current_model_ports > count)
619 auto res = avnd_input_idx_to_iterator(idx);
621 auto begin_deleted = res;
622 for(
int i = 0; i < (current_model_ports - count); i++)
624 to_delete.push_back(*res);
627 m_inlets.erase(begin_deleted, res);
630 dynamic_ports.num_in_ports(idx) = count;
632 removeCablesOfPorts(to_delete);
634 for(
auto port : to_delete)
638 template <
typename P, std::
size_t N>
639 void request_new_dynamic_output_count(P& port, avnd::field_index<N> idx,
int count)
641 const int current_model_ports = dynamic_ports.num_out_ports(idx);
642 if(current_model_ports == count || count < 0 || count > 512)
645 ossia::small_pod_vector<Process::Outlet*, 4> to_delete;
646 if(current_model_ports < count)
650 auto res = avnd_output_idx_to_iterator(idx);
651 res += current_model_ports;
653 OutletInitFunc<Info> outlets{*
this, outlets_to_add};
654 outlets.outlet = 1000000 + N * 1000 + current_model_ports;
656 for(
int i = current_model_ports; i < count; i++)
659 if(std::ssize(outlets_to_add) > sz)
661 sz = std::ssize(outlets_to_add);
662 auto new_outlet = outlets_to_add.back();
663 if(
auto nm = new_outlet->name(); nm.contains(
"{}"))
665 nm.replace(
"{}", QString::number(i));
666 new_outlet->setName(nm);
670 m_outlets.insert(res, outlets_to_add.begin(), outlets_to_add.end());
672 else if(current_model_ports > count)
675 auto res = avnd_output_idx_to_iterator(idx);
677 auto begin_deleted = res;
678 for(
int i = 0; i < (current_model_ports - count); i++)
680 to_delete.push_back(*res);
683 m_outlets.erase(begin_deleted, res);
686 dynamic_ports.num_out_ports(idx) = count;
688 removeCablesOfPorts(to_delete);
690 for(
auto port : to_delete)
694 void init_all_ports()
696 InletInitFunc<Info> inlets{*
this, m_inlets};
697 OutletInitFunc<Info> outlets{*
this, m_outlets};
698 avnd::port_visit_dispatcher<Info>([&inlets]<
typename P>(P&& port,
auto idx) {
699 if constexpr(!avnd::dynamic_ports_port<P>)
701 }, [&outlets]<
typename P>(P&& port,
auto idx) {
702 if constexpr(!avnd::dynamic_ports_port<P>)
706 if(!
requires { Info::ossia_show_ports_by_default; })
708 hideAllInlets(*
this);
712 int expected_input_ports() const noexcept
718 if constexpr(avnd::audio_argument_processor<Info>)
720 else if constexpr(avnd::tag_cv<Info>)
724 count += avnd::messages_introspection<Info>::size;
726 avnd::input_introspection<Info>::for_all([
this, &count]<std::size_t Idx,
typename P>(
727 avnd::field_reflection<Idx, P> field) {
729 if constexpr(avnd::dynamic_ports_port<P>)
730 num_ports = dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
737 int expected_output_ports() const noexcept
743 if constexpr(avnd::audio_argument_processor<Info>)
745 else if constexpr(avnd::tag_cv<Info>)
747 using operator_ret =
typename avnd::function_reflection_o<Info>::return_type;
748 if constexpr(!std::is_void_v<operator_ret>)
752 avnd::output_introspection<Info>::for_all(
754 &count]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
756 if constexpr(avnd::dynamic_ports_port<P>)
757 num_ports = dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
764 std::span<Process::Inlet*> avnd_input_idx_to_model_ports(
int index)
const noexcept
770 if constexpr(avnd::audio_argument_processor<Info>)
772 else if constexpr(avnd::tag_cv<Info>)
776 model_index += avnd::messages_introspection<Info>::size;
778 std::span<Process::Inlet*> ret;
779 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size == 0)
781 ret = std::span<Process::Inlet*>(
782 const_cast<Process::Inlet**
>(this->m_inlets.data()) + model_index + index, 1);
786 avnd::input_introspection<Info>::for_all(
787 [
this, index, &model_index,
788 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
792 if constexpr(avnd::dynamic_ports_port<P>)
794 num_ports = dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
801 ret = std::span<Process::Inlet*>(
802 const_cast<Process::Inlet**
>(this->m_inlets.data()) + model_index,
807 if constexpr(avnd::dynamic_ports_port<P>)
809 model_index += dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
822 std::span<Process::Outlet*> avnd_output_idx_to_model_ports(
int index)
const noexcept
828 if constexpr(avnd::audio_argument_processor<Info>)
830 else if constexpr(avnd::tag_cv<Info>)
832 using operator_ret =
typename avnd::function_reflection_o<Info>::return_type;
833 if constexpr(!std::is_void_v<operator_ret>)
838 model_index += avnd::messages_introspection<Info>::size;
840 std::span<Process::Outlet*> ret;
841 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size == 0)
843 ret = std::span<Process::Outlet*>(
844 const_cast<Process::Outlet**
>(this->m_outlets.data()) + model_index + index,
849 avnd::output_introspection<Info>::for_all(
850 [
this, index, &model_index,
851 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
855 if constexpr(avnd::dynamic_ports_port<P>)
857 num_ports = dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
864 ret = std::span<Process::Outlet*>(
870 if constexpr(avnd::dynamic_ports_port<P>)
872 model_index += dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
885 Process::Inlets::iterator avnd_input_idx_to_iterator(
int index)
const noexcept
892 if constexpr(avnd::audio_argument_processor<Info>)
894 else if constexpr(avnd::tag_cv<Info>)
898 model_index += avnd::messages_introspection<Info>::size;
900 Process::Inlets::iterator ret;
901 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size == 0)
903 ret =
const_cast<ProcessModel*
>(
this)->m_inlets.begin() + model_index;
907 avnd::input_introspection<Info>::for_all(
908 [
this, index, &model_index,
909 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
912 ret =
const_cast<ProcessModel*
>(
this)->m_inlets.begin() + model_index;
916 if constexpr(avnd::dynamic_ports_port<P>)
918 model_index += dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
930 Process::Outlets::iterator avnd_output_idx_to_iterator(
int index)
const noexcept
938 if constexpr(avnd::audio_argument_processor<Info>)
940 else if constexpr(avnd::tag_cv<Info>)
942 using operator_ret =
typename avnd::function_reflection_o<Info>::return_type;
943 if constexpr(!std::is_void_v<operator_ret>)
947 Process::Outlets::iterator ret;
948 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size == 0)
950 ret =
const_cast<ProcessModel*
>(
this)->m_outlets.begin() + model_index;
954 avnd::output_introspection<Info>::for_all(
955 [
this, index, &model_index,
956 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
959 ret =
const_cast<ProcessModel*
>(
this)->m_outlets.begin() + model_index;
963 if constexpr(avnd::dynamic_ports_port<P>)
965 model_index += dynamic_ports.num_out_ports(avnd::field_index<Idx>{});