116 return uuid_from_string<Node>();
123 return static_key() == other || Execution::ProcessComponent::base_key_match(other);
126#if defined(SCORE_PLUGIN_GFX)
127 [[no_unique_address]] ossia::type_if<int, is_gpu<Node>> node_id = score::gfx::invalid_node_index;
132 element, ctx,
"Executor::ProcessModel<Info>", p}
137 setup_gpu(element, ctx, p);
142 setup_cpu(element, ctx, p);
145 if constexpr(avnd::tag_process_exec<Node>)
147 this->m_ossia_process = std::make_shared<CustomNodeProcess<Node>>(this->node);
151 this->m_ossia_process = std::make_shared<ossia::node_process>(this->node);
159 = *ctx.doc.findPlugin<Explorer::DeviceDocumentPlugin>()->networkContext();
163 auto st = ossia::exec_state_facade{ctx.execState.get()};
164 std::shared_ptr<safe_node<Node>> ptr;
165 auto node =
new safe_node<Node>{st.bufferSize(), (double)st.sampleRate(),
id};
166 node->root_inputs().reserve(element.inlets().size());
167 node->root_outputs().reserve(element.outlets().size());
169 node->prepare(*ctx.execState.get());
171 if_possible(node->impl.effect.ossia_state = st);
172 if_possible(node->impl.effect.io_context = &net_ctx.context);
173 if_possible(node->impl.effect.ossia_document_context = &ctx.doc);
177 if constexpr(
requires { ptr->impl.effect; })
178 if constexpr(std::is_same_v<std::decay_t<
decltype(ptr->impl.effect)>, Node>)
179 connect_message_bus(element, ctx, ptr->impl.effect);
180 connect_worker(ctx, ptr->impl);
182 node->dynamic_ports = element.dynamic_ports;
185 connect_controls(element, ctx, ptr);
186 update_controls(ptr);
188 &element, &Process::ProcessModel::inletsChanged,
this,
189 &Executor::recompute_ports);
191 &element, &Process::ProcessModel::outletsChanged,
this,
192 &Executor::recompute_ports);
195 node->audio_configuration_changed(st);
197 m_oldInlets = element.inlets();
198 m_oldOutlets = element.outlets();
214 using Gfx::gfx_exec_node::gfx_exec_node;
215 std::string label()
const noexcept override
217 return std::string(avnd::get_name<Node>());
221 auto node = std::make_shared<named_exec_node>(gfx_exec);
222 node->prepare(*ctx.execState);
229 for(
auto& ctl : element.inlets())
231 if(
auto ctrl = qobject_cast<Process::ControlInlet*>(ctl))
233 auto& p = node->add_control();
234 p->value = ctrl->value();
238 ctrl, &Process::ControlInlet::valueChanged,
this,
242 else if(
auto ctrl = qobject_cast<Process::ValueInlet*>(ctl))
244 auto& p = node->add_control();
248 else if(
auto ctrl = qobject_cast<Process::AudioInlet*>(ctl))
252 else if(
auto ctrl = qobject_cast<Gfx::TextureInlet*>(ctl))
254 ossia::texture_inlet& inl = *node->add_texture();
255 ctrl->setupExecution(inl,
this);
257 else if(
auto ctrl = qobject_cast<Gfx::GeometryInlet*>(ctl))
259 ossia::geometry_inlet& inl = *node->add_geometry();
260 ctrl->setupExecution(inl,
this);
265 for(
auto* outlet : element.outlets())
267 if(
auto ctrl = qobject_cast<Process::ControlOutlet*>(outlet))
269 node->add_control_out();
271 else if(
auto ctrl = qobject_cast<Process::ValueOutlet*>(outlet))
273 node->add_control_out();
275 else if(
auto out = qobject_cast<Gfx::TextureOutlet*>(outlet))
277 node->add_texture_out();
278 out->nodeId = node_id;
280 else if(
auto out = qobject_cast<Gfx::GeometryOutlet*>(outlet))
282 node->add_geometry_out();
287 std::weak_ptr qex_ptr = std::shared_ptr<Execution::ExecutionCommandQueue>(
288 ctx.alias.lock(), &ctx.executionQueue);
289 std::unique_ptr<score::gfx::Node> ptr;
292 auto gpu_node =
new CustomGpuNode<Node>(qex_ptr, node->control_outs,
id, ctx.doc);
297 auto gpu_node =
new GpuComputeNode<Node>(qex_ptr, node->control_outs,
id, ctx.doc);
303 =
new GfxNode<Node>(element, qex_ptr, node->control_outs,
id, ctx.doc);
308 for(
auto& ctl : element.inlets())
310 if(
auto ctrl = qobject_cast<Gfx::TextureInlet*>(ctl))
312 ossia::texture_inlet& inl
313 =
static_cast<ossia::texture_inlet&
>(*node->root_inputs()[i]);
314 ptr->process(i, inl.data);
318 node->id = gfx_exec.ui->register_node(std::move(ptr));
323 void recompute_ports()
326 auto n = std::dynamic_pointer_cast<safe_node<Node>>(this->node);
331 in_exec([dp = this->process().dynamic_ports, node = n] {
332 node->dynamic_ports = dp;
333 node->root_inputs().clear();
334 node->root_outputs().clear();
335 node->initialize_all_ports();
339 void connect_controls(
341 std::shared_ptr<safe_node<Node>>& ptr)
343 using dynamic_ports_port_type = avnd::dynamic_ports_input_introspection<Node>;
344 using control_inputs_type = avnd::control_input_introspection<Node>;
345 using curve_inputs_type = avnd::curve_input_introspection<Node>;
346 using soundfile_inputs_type = avnd::soundfile_input_introspection<Node>;
347 using midifile_inputs_type = avnd::midifile_input_introspection<Node>;
348 using raw_file_inputs_type = avnd::raw_file_input_introspection<Node>;
349 using control_outputs_type = avnd::control_output_introspection<Node>;
352 safe_node<Node>& node = *ptr;
353 avnd::effect_container<Node>& eff = node.impl;
358 if constexpr(dynamic_ports_port_type::size > 0)
360 for(
auto state : eff.full_state())
362 dynamic_ports_port_type::for_all_n2(
366 if constexpr(control_inputs_type::size > 0)
368 for(
auto state : eff.full_state())
370 control_inputs_type::for_all_n2(
374 if constexpr(curve_inputs_type::size > 0)
376 for(
auto state : eff.full_state())
378 curve_inputs_type::for_all_n2(
382 if constexpr(soundfile_inputs_type::size > 0)
384 soundfile_inputs_type::for_all_n2(
385 avnd::get_inputs<Node>(eff),
388 setup_soundfile_task_pool(element, ctx, ptr);
390 if constexpr(midifile_inputs_type::size > 0)
392 midifile_inputs_type::for_all_n2(
393 avnd::get_inputs<Node>(eff),
396 if constexpr(raw_file_inputs_type::size > 0)
398 raw_file_inputs_type::for_all_n2(
399 avnd::get_inputs<Node>(eff),
404 if constexpr(control_inputs_type::size > 0 || control_outputs_type::size > 0)
407 if(settings.getExecutionUpdate())
410 std::weak_ptr<safe_node<Node>> weak_node = ptr;
414 con(ctx.doc.coarseUpdateTimer, &QTimer::timeout,
this,
415 [timer_action = std::move(timer_action)] { timer_action(); },
416 Qt::QueuedConnection);
421 void setup_soundfile_task_pool(
423 std::shared_ptr<safe_node<Node>>& ptr)
425 safe_node<Node>& node = *ptr;
427 using soundfile_inputs_type = avnd::soundfile_input_introspection<Node>;
429 auto& tq = score::TaskPool::instance();
430 node.soundfiles.load_request
431 = [&tq, p = std::weak_ptr{ptr}, &ctx](std::string& str,
int idx) {
432 auto eff_ptr = p.lock();
435 tq.post([eff_ptr = std::move(eff_ptr), filename = str, &ctx, idx]()
mutable {
436 if(
auto file = loadSoundfile(filename, ctx.doc, ctx.execState))
438 ctx.executionQueue.enqueue(
439 [sf = std::move(file), p = std::weak_ptr{eff_ptr}, idx]()
mutable {
440 auto eff_ptr = p.lock();
444 avnd::effect_container<Node>& eff = eff_ptr->impl;
445 soundfile_inputs_type::for_nth_mapped_n2(
446 avnd::get_inputs<Node>(eff), idx,
447 [&]<std::size_t NField, std::size_t N>(
448 auto& field, avnd::predicate_index<N> p,
449 avnd::field_index<NField> f) {
450 eff_ptr->soundfile_loaded(sf, p, f);
458 void connect_message_bus(
462 if constexpr(avnd::has_gui_to_processor_bus<Node>)
464 element.from_ui = [qex_ptr = weak_exec, &eff](QByteArray b) {
465 auto qex = qex_ptr.lock();
469 qex->enqueue([mess = std::move(b), &eff]()
mutable {
470 using refl = avnd::function_reflection<&Node::process_message>;
471 static_assert(refl::count <= 1);
473 if constexpr(refl::count == 0)
476 eff.process_message();
478 else if constexpr(refl::count == 1)
480 using arg_type = avnd::first_argument<&Node::process_message>;
481 std::decay_t<arg_type> arg;
484 eff.process_message(std::move(arg));
490 if constexpr(avnd::has_processor_to_gui_bus<Node>)
492 if constexpr(
requires { eff.send_message = [](
auto&&) { }; })
494 eff.send_message = [proc = QPointer{&this->process()},
495 qed_ptr = weak_edit]<
typename T>(T&& b)
mutable {
496 auto qed = qed_ptr.lock();
500 sizeof(QPointer<QObject>) +
sizeof(b)
501 < Execution::ExecutionCommand::max_storage)
503 qed->enqueue([proc, bb = std::move(b)]()
mutable {
504 if(proc && proc->to_ui)
511 [proc, bb = std::make_unique<std::decay_t<T>>(std::move(b))]()
mutable {
512 if(proc && proc->to_ui)
518 else if constexpr(
requires { eff.send_message = []() { }; })
521 = [proc = QPointer{&this->process()}, qed_ptr = weak_edit]()
mutable {
524 auto qed = qed_ptr.lock();
528 qed->enqueue([proc]()
mutable {
529 if(proc && proc->to_ui)
537 void connect_worker(const ::Execution::Context& ctx, avnd::effect_container<Node>& eff)
539 if constexpr(avnd::has_worker<Node>)
542 auto& tq = score::TaskPool::instance();
543 using worker_type =
decltype(eff.effect.worker);
544 for(
auto& eff : eff.effects())
546 std::weak_ptr eff_ptr = std::shared_ptr<Node>(this->node, &eff);
547 std::weak_ptr qex_ptr = std::shared_ptr<Execution::ExecutionCommandQueue>(
548 ctx.alias.lock(), &ctx.executionQueue);
551 = [&tq, qex_ptr = std::move(qex_ptr),
552 eff_ptr = std::move(eff_ptr)]<
typename... Args>(Args&&... f)
mutable {
555 tq.post([eff_ptr, qex_ptr, ... ff = std::forward<Args>(f)]()
mutable {
563 =
decltype(worker_type::work(std::forward<
decltype(ff)>(ff)...));
564 if constexpr(std::is_void_v<type_of_result>)
566 worker_type::work(std::forward<
decltype(ff)>(ff)...);
572 auto res = worker_type::work(std::forward<
decltype(ff)>(ff)...);
578 ossia::qt::run_async(
579 qApp, [eff_ptr = std::move(eff_ptr), qex_ptr = std::move(qex_ptr),
580 res = std::move(res)]()
mutable {
582 std::shared_ptr qex = qex_ptr.lock();
587 [eff_ptr = std::move(eff_ptr), res = std::move(res)]()
mutable {
591 if(
auto p = eff_ptr.lock())
603 void update_controls(std::shared_ptr<safe_node<Node>>& ptr)
605 avnd::effect_container<Node>& eff = ptr->impl;
607 for(
auto state : eff.full_state())
609 avnd::input_introspection<Node>::for_all(
610 state.inputs, [&](
auto& field) { if_possible(field.update(state.effect)); });
615 void cleanup()
override
617 if constexpr(
requires { this->process().from_ui; })
619 this->process().from_ui = [](QByteArray arr) {};
627 auto& gfx_exec = this->system().doc.template plugin<Gfx::DocumentPlugin>().exec;
629 gfx_exec.ui->unregister_node(node_id);
633 for(
auto* outlet : this->process().outlets())
635 if(
auto out = qobject_cast<Gfx::TextureOutlet*>(outlet))
641 ::Execution::ProcessComponent::cleanup();