124 SCORE_SERIALIZE_FRIENDS
127 friend struct
TSerializer<JSONObject, oscr::ProcessModel<Info>>;
130 [[no_unique_address]]
131 oscr::dynamic_ports_storage<Info> dynamic_ports;
133 [[no_unique_address]]
134 ossia::type_if<Info, oscr::has_ports_callbacks<Info>>
135 object_storage_for_ports_callbacks;
138 const TimeVal& duration, const Id<Process::ProcessModel>& id,
139 const score::DocumentContext& ctx, QObject* parent)
143 metadata().setInstanceName(*
this);
146 init_before_port_creation();
148 init_after_port_creation();
152 const TimeVal& duration,
const QString& custom,
157 metadata().setInstanceName(*
this);
160 init_before_port_creation();
163 if constexpr(avnd::file_input_introspection<Info>::size > 0)
165 static constexpr auto idx
166 = avnd::file_input_introspection<Info>::index_to_field_index(0);
167 setupInitialStringPort(idx, custom);
169 else if constexpr(avnd::control_input_introspection<Info>::size > 0)
171 static constexpr auto idx
172 = avnd::control_input_introspection<Info>::index_to_field_index(0);
174 typename avnd::control_input_introspection<Info>::template nth_element<0>;
175 if constexpr(avnd::string_ish<
decltype(type::value)>)
176 setupInitialStringPort(idx, custom);
178 init_after_port_creation();
181 void setupInitialStringPort(
int idx,
const QString& custom)
noexcept
184 auto pp = safe_cast<Process::ControlInlet*>(port);
186 if(
auto val = pp->value(); bool(val.target<std::string>()))
188 pp->setValue(custom.toStdString());
192 template <
typename Impl>
197 init_before_port_creation();
202 if(!
requires { Info::ossia_show_ports_by_default; })
203 for(
auto& p : this->outlets())
204 p->displayHandledExplicitly =
false;
205 upgrade_value_inlets_to_controls();
206 upgrade_controls_to_value_inlets();
207 upgrade_changed_control_types();
208 init_after_port_creation();
226 const rapidjson::Document doc = readJson(preset.data);
229 for(
const auto& arr : doc.GetArray())
231 if(!arr.IsArray() || arr.Size() < 2 || !arr[0].IsInt())
234 if(arr.Size() >= 3 && arr[2].IsString())
235 ctrl = preset_control(
237 QString::fromUtf8(arr[2].GetString(), qsizetype(arr[2].GetStringLength())));
239 ctrl = unnamed_preset_control(arr[0].GetInt());
242 ossia::value val =
JsonValue{arr[1]}.to<ossia::value>();
243 upgrade_preset_value(*ctrl, val);
252 p.name = this->metadata().getName();
253 p.key.key = this->concreteKey();
256 r.stream.StartArray();
257 for(
const auto& inlet : m_inlets)
259 if(
auto ctrl = qobject_cast<Process::ControlInlet*>(inlet))
261 r.stream.StartArray();
262 r.stream.Int(ctrl->id().val());
264 const QByteArray name = ctrl->name().toUtf8();
265 r.stream.String(name.data(), rapidjson::SizeType(name.size()));
270 p.data = r.toByteArray();
278 for(
auto* in : m_inlets)
280 if(in->name() != name)
282 if(
auto ctrl = qobject_cast<Process::ControlInlet*>(in))
284 if(ctrl->id().val() ==
id)
295 if(
auto ctrl = control_named(name,
id))
298 avnd::input_introspection<Info>::for_all(
299 [&]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
300 if constexpr(
requires { P::former_name(); } &&
requires { P::name(); })
302 const auto was = P::former_name();
303 if(res || name != QString::fromUtf8(was.data(), qsizetype(was.size())))
305 const auto now = P::name();
306 res = control_named(QString::fromUtf8(now.data(), qsizetype(now.size())),
id);
314 if constexpr(
requires { Info::removed_inlets(); })
318 static constexpr auto removed = Info::removed_inlets();
327 if(position < 0 || position >= std::ssize(m_inlets))
329 return qobject_cast<Process::ControlInlet*>(m_inlets[
position]);
333 return qobject_cast<Process::ControlInlet*>(
340 template <
typename P>
341 static std::optional<std::string> current_choice(
const std::string& saved)
343 static constexpr auto range = avnd::get_range<P>();
344 const auto names = avnd::to_enum_range(
range.values);
345 if(ossia::any_of(names, [&](
const auto& n) {
return n == saved; }))
347 const auto former = P::former_values();
348 for(std::size_t i = 0; i < former.size() && i < names.size(); i++)
349 if(former[i] == saved)
350 return std::string(names[i]);
356 avnd::input_introspection<Info>::for_all(
357 [&]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
358 if constexpr(
requires { P::former_values(); })
360 auto ports = avnd_input_idx_to_model_ports(Idx);
361 if(ports.size() == 1 && ports[0] == &ctrl)
362 if(
const auto* str = val.target<std::string>())
363 if(
auto now = current_choice<P>(*str))
364 val = std::move(*now);
367 if(val.get_type() == ctrl.value().get_type())
369 avnd::input_introspection<Info>::for_all(
370 [&]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
371 if constexpr(
requires(
const ossia::value& v) { P::upgrade_value(v); })
373 auto ports = avnd_input_idx_to_model_ports(Idx);
374 if(ports.size() == 1 && ports[0] == &ctrl)
375 val = P::upgrade_value(std::as_const(val));
377 else if constexpr(
requires(
float f) {
378 { P::upgrade_value(f) } -> std::convertible_to<float>;
381 auto ports = avnd_input_idx_to_model_ports(Idx);
382 if(ports.size() == 1 && ports[0] == &ctrl && is_number(val))
383 if(
auto v = carried_value(
384 float(P::upgrade_value(ossia::convert<float>(val))), ctrl))
392 if constexpr(avnd::tag_loops_by_default<Info>)
396 void init_before_port_creation() { init_dynamic_ports(); }
397 void init_after_port_creation()
399 init_controller_ports();
400 init_folder_comboboxes();
401 init_single_cable_ports();
402 init_time_choosers();
406 void init_time_choosers()
408 avnd::input_introspection<Info>::for_all(
409 [
this]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
410 if constexpr(oscr::offers_bar_sync<P>)
411 for(
auto* port : avnd_input_idx_to_model_ports(Idx))
412 if(auto* tc = qobject_cast<
Process::TimeChooser*>(port))
413 tc->offersBarSync = true;
417 void init_single_cable_ports()
419 avnd::input_introspection<Info>::for_all(
420 [
this]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
421 if constexpr(single_cable_port<P>)
422 for(
auto* port : avnd_input_idx_to_model_ports(Idx))
423 port->singleCable = true;
431 void init_folder_comboboxes()
433 for(
auto* inl : m_inlets)
435 auto combo = qobject_cast<Process::ComboBox*>(inl);
436 if(!combo || combo->folderPortName.isEmpty())
440 for(
auto* other : m_inlets)
442 auto ci = qobject_cast<Process::ControlInlet*>(other);
443 if(ci && ci != combo && ci->name() == combo->folderPortName)
453 if(
auto s = p->value().target<std::string>())
458 auto path = QString::fromStdString(*s);
465 for(QObject* o = this->parent(); o && !doc; o = o->parent())
466 doc = qobject_cast<score::Document*>(o);
471 if(QFileInfo fi{path}; fi.isFile())
472 return fi.absolutePath();
478 combo->repopulateFromFolder(pathOf(folderPort));
481 folderPort, &Process::ControlInlet::valueChanged, combo,
482 [combo, folderPort, pathOf](
const ossia::value&) {
483 combo->repopulateFromFolder(pathOf(folderPort));
487 void check_all_ports()
489 if(std::ssize(m_inlets) == expected_input_ports()
490 && std::ssize(m_outlets) == expected_output_ports())
492 rename_former_kept_ports();
493 upgrade_former_values();
497 std::vector<Dataflow::SavedPort> oldInlets, oldOutlets;
498 std::vector<saved_port> oldInletInfo, oldOutletInfo;
499 for(
auto& port : m_inlets)
501 oldInlets.emplace_back(
503 oldInletInfo.push_back(saved_port::of(*port));
505 for(
auto& port : m_outlets)
507 oldOutlets.emplace_back(
509 oldOutletInfo.push_back(saved_port::of(*port));
512 qDeleteAll(m_inlets);
514 qDeleteAll(m_outlets);
518 init_dynamic_port_instances();
520 rename_former_ports(oldInlets);
524 std::vector<int> inletMatches, outletMatches;
525 const auto lost = Dataflow::reloadPortsInNewProcess(
526 oldInlets, oldOutlets, *
this, &inletMatches, &outletMatches);
527 keep_saved_ids(m_inlets, inletMatches, oldInletInfo);
528 keep_saved_ids(m_outlets, outletMatches, oldOutletInfo);
529 upgrade_rebuilt_control_kinds(inletMatches, oldInletInfo);
530 upgrade_former_values();
533 const std::string_view type_name = avnd::get_name<Info>();
534 qWarning() << QLatin1String(type_name.data(), type_name.size())
535 << this->metadata().getName()
536 <<
": saved ports lost on load (renamed or removed since):"
547 std::optional<ossia::value> value;
552 saved_port p{port.id().val(), port.concreteKey(), std::nullopt, port.stableIdentity};
553 if(
auto ctrl = qobject_cast<const Process::ControlInlet*>(&port))
554 p.value = ctrl->value();
562 template <
typename Ports>
563 static void keep_saved_ids(
564 Ports& ports,
const std::vector<int>&
matches,
const std::vector<saved_port>& saved)
566 if(
matches.size() != ports.size())
568 std::vector<int> ids(ports.size());
569 std::vector<int> taken;
571 for(std::size_t i = 0; i < ports.size(); i++)
573 next = std::max(next, ports[i]->
id().val() + 1);
574 ids[i] = ports[i]->id().val();
575 if(
matches[i] >= 0 && !ports[i]->stableIdentity && !saved[
matches[i]].stable)
577 if(
matches[i] >= 0 || ports[i]->stableIdentity)
578 taken.push_back(ids[i]);
580 for(
const auto& p : saved)
581 next =
std::max(next, p.id + 1);
582 for(std::size_t i = 0; i < ports.size(); i++)
584 if(
matches[i] < 0 && !ports[i]->stableIdentity)
586 if(ossia::contains(taken, ids[i]))
588 taken.push_back(ids[i]);
590 if(ports[i]->
id().val() != ids[i])
598 void upgrade_rebuilt_control_kinds(
599 const std::vector<int>&
matches,
const std::vector<saved_port>& saved)
601 avnd::input_introspection<Info>::for_all(
602 [&]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
604 avnd::parameter_port<P> && !oscr::ossia_port<P>
605 && !avnd::dynamic_ports_port<P> && avnd::has_widget<P>
606 && !
requires { P::on_controller_interaction(); })
608 auto ports = avnd_input_idx_to_model_ports(Idx);
609 if(ports.size() != 1)
611 const auto pos = std::size_t(ossia::find(m_inlets, ports[0]) - m_inlets.begin());
614 const auto& old = saved[std::size_t(
matches[pos])];
615 auto fresh = qobject_cast<Process::ControlInlet*>(ports[0]);
616 if(!fresh || !old.value || old.key == fresh->concreteKey())
618 if(
auto v = upgraded_value<P>(*old.value, *fresh))
629 void rename_former_ports(std::vector<Dataflow::SavedPort>& saved)
636 std::vector<rename> renames;
637 avnd::input_introspection<Info>::for_all(
638 [&]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
639 if constexpr(
requires { P::former_name(); } &&
requires { P::name(); })
641 const auto now = P::name();
642 const auto was = P::former_name();
644 {QString::fromUtf8(was.data(), qsizetype(was.size())),
645 QString::fromUtf8(now.data(), qsizetype(now.size()))});
648 const auto was_saved = [&](
const QString& name) {
649 return ossia::any_of(saved, [&](
const auto& p) {
return p.name == name; });
651 for(
bool changed =
true; changed;)
654 for(
auto& r : renames)
656 if(r.applies || !was_saved(r.was))
658 const bool taken_by_now = was_saved(r.now)
659 && !ossia::any_of(renames, [&](
const rename& o) {
660 return o.applies && o.was == r.now;
663 changed = r.applies =
true;
667 std::vector<std::pair<std::size_t, QString>> moves;
668 for(
const auto& r : renames)
672 for(std::size_t i = 0; i < saved.size(); i++)
673 if(saved[i].name == r.was
674 && !ossia::any_of(moves, [i](
const auto& m) { return m.first == i; }))
676 moves.emplace_back(i, r.now);
680 for(
auto& [i, name] : moves)
681 saved[i].name = name;
686 void rename_former_kept_ports()
688 avnd::input_introspection<Info>::for_all(
689 [
this]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
690 if constexpr(
requires { P::former_name(); } &&
requires { P::name(); })
692 auto ports = avnd_input_idx_to_model_ports(Idx);
693 if(ports.size() != 1)
695 const auto was = P::former_name();
696 if(ports[0]->name() != QString::fromUtf8(was.data(), qsizetype(was.size())))
698 const auto now = P::name();
699 ports[0]->setName(QString::fromUtf8(now.data(), qsizetype(now.size())));
707 void upgrade_former_values()
709 avnd::input_introspection<Info>::for_all(
710 [
this]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
711 if constexpr(
requires { P::former_values(); })
713 auto ports = avnd_input_idx_to_model_ports(Idx);
714 if(ports.size() != 1)
716 auto inlet = qobject_cast<Process::ControlInlet*>(ports[0]);
719 const ossia::value& value = inlet->value();
720 const auto* str = value.template target<std::string>();
723 if(
auto now = current_choice<P>(*str))
724 inlet->setValue(std::move(*now));
736 template <
typename F, std::
size_t Idx>
739 auto ports = avnd_input_idx_to_model_ports(Idx);
740 SCORE_ASSERT(ports.size() == 1);
741 auto old_inlet = qobject_cast<Process::ControlInlet*>(ports[0]);
742 SCORE_ASSERT(old_inlet);
745 InletInitFunc<Info> make{*
this, fresh};
746 make.inlet = old_inlet->id().val();
748 if(fresh.size() != 1)
754 auto new_inlet = qobject_cast<Process::ControlInlet*>(fresh[0]);
755 if(!new_inlet || new_inlet->concreteKey() == old_inlet->concreteKey())
761 new_inlet->loadData(old_inlet->saveData(), Process::PortLoadDataFlags::ReloadValue);
762 new_inlet->setExposed(old_inlet->exposed());
763 new_inlet->setDescription(old_inlet->description());
764 new_inlet->displayHandledExplicitly = old_inlet->displayHandledExplicitly;
766 auto it = ossia::find(m_inlets, old_inlet);
767 SCORE_ASSERT(it != m_inlets.end());
778 static bool is_number(
const ossia::value& v)
noexcept
780 const auto t = v.get_type();
781 return t == ossia::val_type::FLOAT || t == ossia::val_type::INT
782 || t == ossia::val_type::BOOL;
785 static std::optional<ossia::value>
788 const auto from = old.get_type();
789 const auto to = fresh.value().get_type();
794 if(qobject_cast<const Process::TimeChooser*>(&fresh))
795 return ossia::vec2f{ossia::convert<float>(old), 0.f};
798 case ossia::val_type::FLOAT:
799 return ossia::convert<float>(old);
800 case ossia::val_type::INT:
801 return int(std::lround(ossia::convert<float>(old)));
802 case ossia::val_type::BOOL:
803 return ossia::convert<bool>(old);
811 template <
typename P>
812 static std::optional<ossia::value>
815 if constexpr(
requires(
float f) {
816 { P::upgrade_value(f) } -> std::convertible_to<float>;
819 if(!is_number(old_value))
821 return carried_value(
float(P::upgrade_value(ossia::convert<float>(old_value))), fresh);
823 else if constexpr(
requires { P::upgrade_value(old_value); })
824 return P::upgrade_value(old_value);
826 return carried_value(old_value, fresh);
835 void upgrade_changed_control_types()
837 avnd::input_introspection<Info>::for_all(
838 [
this]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
840 avnd::parameter_port<P> && !oscr::ossia_port<P>
841 && !avnd::dynamic_ports_port<P> && avnd::has_widget<P>
842 && std::is_default_constructible_v<P>
845 && !
requires { P::on_controller_interaction(); })
847 auto ports = avnd_input_idx_to_model_ports(Idx);
848 if(ports.size() != 1)
850 auto old_inlet = qobject_cast<Process::ControlInlet*>(ports[0]);
855 InletInitFunc<Info> make{*
this, fresh};
856 make.inlet = old_inlet->id().val();
857 make(P{}, avnd::field_index<Idx>{});
858 auto new_inlet = fresh.size() == 1
859 ? qobject_cast<Process::ControlInlet*>(fresh[0])
861 if(!new_inlet || new_inlet->concreteKey() == old_inlet->concreteKey())
867 new_inlet->setExposed(old_inlet->exposed());
868 new_inlet->setAddress(old_inlet->address());
869 if(
auto v = upgraded_value<P>(old_inlet->value(), *new_inlet))
870 new_inlet->setValue(*v);
873 new_inlet->takeCables(std::move(*old_inlet));
874 auto it = ossia::find(m_inlets, old_inlet);
875 SCORE_ASSERT(it != m_inlets.end());
885 void upgrade_controls_to_value_inlets()
887 avnd::input_introspection<Info>::for_all(
888 [
this]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
890 avnd::parameter_port<P> && !oscr::ossia_port<P>
891 && !avnd::dynamic_ports_port<P> && !avnd::has_widget<P>
892 && std::is_default_constructible_v<P>)
894 auto ports = avnd_input_idx_to_model_ports(Idx);
895 if(ports.size() != 1)
897 auto old_inlet = qobject_cast<Process::ControlInlet*>(ports[0]);
902 InletInitFunc<Info> make{*
this, fresh};
903 make.inlet = old_inlet->id().val();
904 make(P{}, avnd::field_index<Idx>{});
906 = fresh.size() == 1 ? qobject_cast<Process::ValueInlet*>(fresh[0]) : nullptr;
913 new_inlet->setExposed(old_inlet->exposed());
914 new_inlet->setAddress(old_inlet->address());
915 new_inlet->takeCables(std::move(*old_inlet));
916 auto it = ossia::find(m_inlets, old_inlet);
917 SCORE_ASSERT(it != m_inlets.end());
924 void upgrade_value_inlets_to_controls()
926 avnd::input_introspection<Info>::for_all(
927 [
this]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P>) {
929 avnd::parameter_port<P> && !oscr::ossia_port<P>
930 && !avnd::dynamic_ports_port<P> && avnd::has_widget<P>
931 && std::is_default_constructible_v<P>)
932 upgrade_value_inlet(P{}, avnd::field_index<Idx>{});
936 template <
typename F, std::
size_t Idx>
937 void upgrade_value_inlet(
const F& field, avnd::field_index<Idx> idx)
939 auto ports = avnd_input_idx_to_model_ports(Idx);
940 if(ports.size() != 1)
942 auto old_inlet = ports[0];
943 if(qobject_cast<Process::ControlInlet*>(old_inlet)
944 || !qobject_cast<Process::ValueInlet*>(old_inlet))
948 InletInitFunc<Info> make{*
this, fresh};
949 make.inlet = old_inlet->id().val();
952 = fresh.size() == 1 ? qobject_cast<Process::ControlInlet*>(fresh[0]) : nullptr;
959 new_inlet->loadData(old_inlet->saveData());
960 new_inlet->setExposed(old_inlet->exposed());
962 auto it = ossia::find(m_inlets, old_inlet);
963 SCORE_ASSERT(it != m_inlets.end());
968 void init_controller_ports()
972 avnd::control_input_introspection<Info>::for_all_n2(
973 avnd::get_inputs<Info>((Info&)this->object_storage_for_ports_callbacks),
974 [
this]<std::size_t Idx,
typename F>(
975 F& field,
auto pred_index, avnd::field_index<Idx> idx) {
976 Info& obj = this->object_storage_for_ports_callbacks;
977 if constexpr(
requires { F::on_controller_interaction(); })
979 upgradeControllerPort(field, idx);
982 if constexpr(
requires(ossia::value v) { F::migrate_value(v); })
984 auto inlet = qobject_cast<Process::ControlInlet*>(
985 avnd_input_idx_to_model_ports(Idx)[0]);
986 inlet->setValue(F::migrate_value(inlet->value()));
989 if constexpr(
requires { F::on_controller_setup(); })
991 auto controller_inlets = avnd_input_idx_to_model_ports(Idx);
992 SCORE_ASSERT(controller_inlets.size() == 1);
993 auto inlet = qobject_cast<Process::ControlInlet*>(controller_inlets[0]);
995 oscr::from_ossia_value(inlet->value(), field.value);
997 if_possible(field.update(obj));
999 F::on_controller_setup()(obj, field.value);
1001 if constexpr(
requires { F::on_controller_interaction(); })
1003 auto controller_inlets = avnd_input_idx_to_model_ports(Idx);
1004 SCORE_ASSERT(controller_inlets.size() == 1);
1005 auto inlet = qobject_cast<Process::ControlInlet*>(controller_inlets[0]);
1006 inlet->noValueChangeOnMove =
true;
1009 if constexpr(!
requires { F::on_controller_setup(); })
1011 oscr::from_ossia_value(inlet->value(), field.value);
1012 if_possible(field.update(obj));
1013 F::on_controller_interaction()(obj, field.value);
1017 inlet, &Process::ControlInlet::valueChanged,
1018 [
this, &field](
const ossia::value& val) {
1019 Info& obj = this->object_storage_for_ports_callbacks;
1020 oscr::from_ossia_value(val, field.value);
1022 if_possible(field.update(obj));
1024 F::on_controller_interaction()(obj, field.value);
1029 if constexpr(avnd::has_gui_to_processor_bus<Info>)
1034 if constexpr(avnd::has_processor_to_gui_bus<Info>)
1036 Info& obj = this->object_storage_for_ports_callbacks;
1038 obj.send_message = [
this]<
typename T>(T&& b)
mutable {
1040 MessageBusSender{this->to_ui}(std::move(b));
1046 void init_dynamic_ports()
1082 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
1084 Info& obj = object_storage_for_ports_callbacks;
1085 avnd::dynamic_ports_input_introspection<Info>::for_all_n2(
1086 avnd::get_inputs(obj),
1087 [&]<std::size_t N>(
auto& port,
auto pred_idx, avnd::field_index<N> field_idx) {
1088 port.request_port_resize = [
this, &port](
int new_count) {
1089 this->request_new_dynamic_input_count(port, avnd::field_index<N>{}, new_count);
1091 if constexpr(
requires { port.request_port_rows; })
1092 port.request_port_rows = [
this, &port](
const auto& rows) {
1093 this->
template request_dynamic_rows<true>(
1094 port, avnd::field_index<N>{}, rows);
1099 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
1101 Info& obj = object_storage_for_ports_callbacks;
1102 avnd::dynamic_ports_output_introspection<Info>::for_all_n2(
1103 avnd::get_outputs(obj),
1104 [&]<std::size_t N>(
auto& port,
auto pred_idx, avnd::field_index<N> field_idx) {
1105 port.request_port_resize = [
this, &port](
int new_count) {
1106 this->request_new_dynamic_output_count(
1107 port, avnd::field_index<N>{}, new_count);
1109 if constexpr(
requires { port.request_port_rows; })
1110 port.request_port_rows = [
this, &port](
const auto& rows) {
1111 this->
template request_dynamic_rows<false>(
1112 port, avnd::field_index<N>{}, rows);
1118 template <
bool Input,
typename P, std::
size_t N,
typename Rows>
1119 void request_dynamic_rows(P& port, avnd::field_index<N> idx,
const Rows& rows)
1121 if(rows.size() > 512)
1123 std::vector<int> keys;
1124 keys.reserve(rows.size());
1125 for(
const auto& [key, text] : rows)
1127 if(key < 10000 || ossia::contains(keys, key))
1129 keys.push_back(key);
1131 auto& all = [&]() ->
auto& {
1139 return avnd_input_idx_to_iterator(idx);
1141 return avnd_output_idx_to_iterator(idx);
1143 auto& count = [&]() ->
auto& {
1145 return dynamic_ports.num_in_ports(idx);
1147 return dynamic_ports.num_out_ports(idx);
1149 using Ports = std::decay_t<
decltype(all)>;
1150 using Port = std::remove_pointer_t<typename Ports::value_type>;
1151 Ports old{first, first + count}, next;
1152 next.reserve(rows.size());
1155 using port_type = std::decay_t<
decltype(port.ports[0])>;
1156 const auto templateName = [] {
1157 const std::string_view nm = avnd::get_name<port_type>();
1158 return QString::fromUtf8(nm.data(), nm.size());
1161 for(
const auto& [key, text] : rows)
1164 = ossia::find_if(old, [key](
auto p) {
return p && p->id().val() == key; });
1166 if(found != old.end())
1176 InletInitFunc<Info> make{*
this, added};
1182 OutletInitFunc<Info> make{*
this, added};
1186 SCORE_ASSERT(added.size() == 1);
1189 p->stableIdentity =
true;
1190 auto name = QString::fromStdString(text);
1191 if(name.trimmed().isEmpty())
1193 name = templateName;
1194 name.replace(QStringLiteral(
"{}"), QString::number(row));
1200 const bool changed = !std::equal(next.begin(), next.end(), first, first + count);
1201 const auto offset = first - all.begin();
1202 all.erase(first, first + count);
1203 all.insert(all.begin() + offset, next.begin(), next.end());
1204 count = rows.size();
1205 ossia::small_pod_vector<Port*, 4> removed;
1208 removed.push_back(p);
1209 removeCablesOfPorts(removed);
1217 for(
auto p : removed)
1225 template <
typename Port_T>
1226 void removeCablesOfPorts(
const ossia::small_pod_vector<Port_T*, 4>& ports)
1234 for(QObject* o = this->parent(); o && !doc; o = o->parent())
1235 doc = qobject_cast<score::Document*>(o);
1240 objs.reserve(ports.size());
1244 const auto& ctx = doc->context();
1245 if(
auto cables = Dataflow::saveCables(objs, ctx); !cables.empty())
1246 Dataflow::removeCables(cables, ctx);
1249 template <
typename P, std::
size_t N>
1250 void request_new_dynamic_input_count(P& port, avnd::field_index<N> idx,
int count)
1252 const int current_model_ports = dynamic_ports.num_in_ports(idx);
1253 if(current_model_ports == count || count < 0 || count > 512)
1256 ossia::small_pod_vector<Process::Inlet*, 4> to_delete;
1257 if(current_model_ports < count)
1261 auto res = avnd_input_idx_to_iterator(idx);
1262 res += current_model_ports;
1264 InletInitFunc<Info> inlets{*
this, inlets_to_add};
1265 inlets.inlet = 10000 + N * 1000 + current_model_ports;
1267 for(
int i = current_model_ports; i < count; i++)
1270 if(std::ssize(inlets_to_add) > sz)
1272 sz = std::ssize(inlets_to_add);
1273 auto new_inlet = inlets_to_add.back();
1274 if(
auto nm = new_inlet->name(); nm.contains(
"{}"))
1276 nm.replace(
"{}", QString::number(i));
1277 new_inlet->setName(nm);
1281 m_inlets.insert(res, inlets_to_add.begin(), inlets_to_add.end());
1283 else if(current_model_ports > count)
1286 auto res = avnd_input_idx_to_iterator(idx);
1288 auto begin_deleted = res;
1289 for(
int i = 0; i < (current_model_ports - count); i++)
1291 to_delete.push_back(*res);
1294 m_inlets.erase(begin_deleted, res);
1297 dynamic_ports.num_in_ports(idx) = count;
1299 removeCablesOfPorts(to_delete);
1301 for(
auto port : to_delete)
1305 template <
typename P, std::
size_t N>
1306 void request_new_dynamic_output_count(P& port, avnd::field_index<N> idx,
int count)
1308 const int current_model_ports = dynamic_ports.num_out_ports(idx);
1309 if(current_model_ports == count || count < 0 || count > 512)
1312 ossia::small_pod_vector<Process::Outlet*, 4> to_delete;
1313 if(current_model_ports < count)
1317 auto res = avnd_output_idx_to_iterator(idx);
1318 res += current_model_ports;
1320 OutletInitFunc<Info> outlets{*
this, outlets_to_add};
1321 outlets.outlet = 1000000 + N * 1000 + current_model_ports;
1323 for(
int i = current_model_ports; i < count; i++)
1326 if(std::ssize(outlets_to_add) > sz)
1328 sz = std::ssize(outlets_to_add);
1329 auto new_outlet = outlets_to_add.back();
1330 if(
auto nm = new_outlet->name(); nm.contains(
"{}"))
1332 nm.replace(
"{}", QString::number(i));
1333 new_outlet->setName(nm);
1337 m_outlets.insert(res, outlets_to_add.begin(), outlets_to_add.end());
1339 else if(current_model_ports > count)
1342 auto res = avnd_output_idx_to_iterator(idx);
1344 auto begin_deleted = res;
1345 for(
int i = 0; i < (current_model_ports - count); i++)
1347 to_delete.push_back(*res);
1350 m_outlets.erase(begin_deleted, res);
1353 dynamic_ports.num_out_ports(idx) = count;
1355 removeCablesOfPorts(to_delete);
1357 for(
auto port : to_delete)
1364 void init_dynamic_port_instances()
1366 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size > 0)
1368 avnd::dynamic_ports_input_introspection<Info>::for_all_n2(
1369 avnd::get_inputs((Info&)object_storage_for_ports_callbacks),
1370 [&]<std::size_t N>(
auto& port,
auto, avnd::field_index<N> idx) {
1371 auto& count = dynamic_ports.num_in_ports(idx);
1372 const int n = std::exchange(count, 0);
1373 request_new_dynamic_input_count(port, idx, n);
1376 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size > 0)
1378 avnd::dynamic_ports_output_introspection<Info>::for_all_n2(
1379 avnd::get_outputs((Info&)object_storage_for_ports_callbacks),
1380 [&]<std::size_t N>(
auto& port,
auto, avnd::field_index<N> idx) {
1381 auto& count = dynamic_ports.num_out_ports(idx);
1382 const int n = std::exchange(count, 0);
1383 request_new_dynamic_output_count(port, idx, n);
1388 void init_all_ports()
1390 InletInitFunc<Info> inlets{*
this, m_inlets};
1391 OutletInitFunc<Info> outlets{*
this, m_outlets};
1392 avnd::port_visit_dispatcher<Info>([&inlets]<
typename P>(P&& port,
auto idx) {
1393 if constexpr(!avnd::dynamic_ports_port<P>)
1395 }, [&outlets]<
typename P>(P&& port,
auto idx) {
1396 if constexpr(!avnd::dynamic_ports_port<P>)
1400 if(!
requires { Info::ossia_show_ports_by_default; })
1406 int expected_input_ports() const noexcept
1412 if constexpr(avnd::audio_argument_processor<Info>)
1414 else if constexpr(avnd::tag_cv<Info>)
1418 count += avnd::messages_introspection<Info>::size;
1420 avnd::input_introspection<Info>::for_all([
this, &count]<std::size_t Idx,
typename P>(
1421 avnd::field_reflection<Idx, P> field) {
1423 if constexpr(avnd::dynamic_ports_port<P>)
1424 num_ports = dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
1431 int expected_output_ports() const noexcept
1437 if constexpr(avnd::audio_argument_processor<Info>)
1439 else if constexpr(avnd::tag_cv<Info>)
1441 using operator_ret =
typename avnd::function_reflection_o<Info>::return_type;
1442 if constexpr(!std::is_void_v<operator_ret>)
1446 avnd::output_introspection<Info>::for_all(
1448 &count]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
1450 if constexpr(avnd::dynamic_ports_port<P>)
1451 num_ports = dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
1458 std::span<Process::Inlet*> avnd_input_idx_to_model_ports(
int index)
const noexcept
1460 int model_index = 0;
1464 if constexpr(avnd::audio_argument_processor<Info>)
1466 else if constexpr(avnd::tag_cv<Info>)
1470 model_index += avnd::messages_introspection<Info>::size;
1472 std::span<Process::Inlet*> ret;
1473 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size == 0)
1475 ret = std::span<Process::Inlet*>(
1476 const_cast<Process::Inlet**
>(this->m_inlets.data()) + model_index + index, 1);
1480 avnd::input_introspection<Info>::for_all(
1481 [
this, index, &model_index,
1482 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
1486 if constexpr(avnd::dynamic_ports_port<P>)
1488 num_ports = dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
1495 ret = std::span<Process::Inlet*>(
1496 const_cast<Process::Inlet**
>(this->m_inlets.data()) + model_index,
1501 if constexpr(avnd::dynamic_ports_port<P>)
1503 model_index += dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
1516 std::span<Process::Outlet*> avnd_output_idx_to_model_ports(
int index)
const noexcept
1518 int model_index = 0;
1522 if constexpr(avnd::audio_argument_processor<Info>)
1524 else if constexpr(avnd::tag_cv<Info>)
1526 using operator_ret =
typename avnd::function_reflection_o<Info>::return_type;
1527 if constexpr(!std::is_void_v<operator_ret>)
1532 model_index += avnd::messages_introspection<Info>::size;
1534 std::span<Process::Outlet*> ret;
1535 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size == 0)
1537 ret = std::span<Process::Outlet*>(
1538 const_cast<Process::Outlet**
>(this->m_outlets.data()) + model_index + index,
1543 avnd::output_introspection<Info>::for_all(
1544 [
this, index, &model_index,
1545 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
1549 if constexpr(avnd::dynamic_ports_port<P>)
1551 num_ports = dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
1558 ret = std::span<Process::Outlet*>(
1564 if constexpr(avnd::dynamic_ports_port<P>)
1566 model_index += dynamic_ports.num_out_ports(avnd::field_index<Idx>{});
1579 Process::Inlets::iterator avnd_input_idx_to_iterator(
int index)
const noexcept
1581 int model_index = 0;
1586 if constexpr(avnd::audio_argument_processor<Info>)
1588 else if constexpr(avnd::tag_cv<Info>)
1592 model_index += avnd::messages_introspection<Info>::size;
1594 Process::Inlets::iterator ret;
1595 if constexpr(avnd::dynamic_ports_input_introspection<Info>::size == 0)
1597 ret =
const_cast<ProcessModel*
>(
this)->m_inlets.begin() + model_index;
1601 avnd::input_introspection<Info>::for_all(
1602 [
this, index, &model_index,
1603 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
1606 ret =
const_cast<ProcessModel*
>(
this)->m_inlets.begin() + model_index;
1610 if constexpr(avnd::dynamic_ports_port<P>)
1612 model_index += dynamic_ports.num_in_ports(avnd::field_index<Idx>{});
1624 Process::Outlets::iterator avnd_output_idx_to_iterator(
int index)
const noexcept
1626 int model_index = 0;
1632 if constexpr(avnd::audio_argument_processor<Info>)
1634 else if constexpr(avnd::tag_cv<Info>)
1636 using operator_ret =
typename avnd::function_reflection_o<Info>::return_type;
1637 if constexpr(!std::is_void_v<operator_ret>)
1641 Process::Outlets::iterator ret;
1642 if constexpr(avnd::dynamic_ports_output_introspection<Info>::size == 0)
1644 ret =
const_cast<ProcessModel*
>(
this)->m_outlets.begin() + model_index;
1648 avnd::output_introspection<Info>::for_all(
1649 [
this, index, &model_index,
1650 &ret]<std::size_t Idx,
typename P>(avnd::field_reflection<Idx, P> field) {
1653 ret =
const_cast<ProcessModel*
>(
this)->m_outlets.begin() + model_index;
1657 if constexpr(avnd::dynamic_ports_port<P>)
1659 model_index += dynamic_ports.num_out_ports(avnd::field_index<Idx>{});