3 #include <Process/Execution/ProcessComponent.hpp>
4 #include <Process/ExecutionContext.hpp>
6 #include <Explorer/DocumentPlugin/DeviceDocumentPlugin.hpp>
8 #include <Crousti/CpuAnalysisNode.hpp>
9 #include <Crousti/CpuFilterNode.hpp>
10 #include <Crousti/CpuGeneratorNode.hpp>
11 #include <Crousti/ExecutorPortSetup.hpp>
12 #include <Crousti/ExecutorUpdateControlValueInUi.hpp>
13 #include <Crousti/File.hpp>
14 #include <Crousti/GpuComputeNode.hpp>
15 #include <Crousti/GpuNode.hpp>
16 #include <Crousti/MessageBus.hpp>
17 #include <Crousti/Metadatas.hpp>
18 #include <Crousti/ProcessModel.hpp>
20 #include <score/tools/Bind.hpp>
22 #include <ossia/dataflow/exec_state_facade.hpp>
23 #include <ossia/dataflow/node_process.hpp>
24 #include <ossia/network/context.hpp>
26 #include <ossia-qt/invoke.hpp>
28 #include <QGuiApplication>
31 #include <Crousti/GpuNode.hpp>
32 #include <Gfx/GfxApplicationPlugin.hpp>
35 #include <score/tools/ThreadPool.hpp>
37 #include <ossia/detail/type_if.hpp>
41 #include <avnd/binding/ossia/data_node.hpp>
42 #include <avnd/binding/ossia/mono_audio_node.hpp>
43 #include <avnd/binding/ossia/node.hpp>
44 #include <avnd/binding/ossia/ossia_audio_node.hpp>
45 #include <avnd/binding/ossia/poly_audio_node.hpp>
46 #include <avnd/concepts/temporality.hpp>
47 #include <avnd/concepts/ui.hpp>
48 #include <avnd/concepts/worker.hpp>
53 template <
typename Node>
56 using node_process::node_process;
59 node_process::start();
60 auto& n =
static_cast<safe_node<Node>&
>(*node);
61 n.impl.effect.start();
65 auto& n =
static_cast<safe_node<Node>&
>(*node);
71 template <
typename Node>
81 return uuid_from_string<Node>();
88 return static_key() == other || Execution::ProcessComponent::base_key_match(other);
91 [[no_unique_address]] ossia::type_if<int, is_gpu<Node>> node_id = -1;
95 element, ctx,
"Executor::ProcessModel<Info>", p}
98 if constexpr(is_gpu<Node>)
100 setup_gpu(element, ctx, p);
105 setup_cpu(element, ctx, p);
108 if constexpr(avnd::tag_process_exec<Node>)
110 this->m_ossia_process = std::make_shared<CustomNodeProcess<Node>>(this->node);
114 this->m_ossia_process = std::make_shared<ossia::node_process>(this->node);
122 = *ctx.doc.findPlugin<Explorer::DeviceDocumentPlugin>()->networkContext();
126 auto st = ossia::exec_state_facade{ctx.execState.get()};
127 std::shared_ptr<safe_node<Node>> ptr;
128 auto node =
new safe_node<Node>{st.bufferSize(), (double)st.sampleRate(),
id};
129 node->root_inputs().reserve(element.inlets().size());
130 node->root_outputs().reserve(element.outlets().size());
132 node->prepare(*ctx.execState.get());
134 if_possible(node->impl.effect.ossia_state = st);
135 if_possible(node->impl.effect.io_context = &net_ctx.context);
139 if constexpr(requires { ptr->impl.effect; })
140 if constexpr(std::is_same_v<std::decay_t<decltype(ptr->impl.effect)>, Node>)
141 connect_message_bus(element, ctx, ptr->impl.effect);
142 connect_worker(ctx, ptr->impl);
144 node->dynamic_ports = element.dynamic_ports;
147 connect_controls(element, ctx, ptr);
148 update_controls(ptr);
150 &element, &Process::ProcessModel::inletsChanged,
this,
151 &Executor::recompute_ports);
153 &element, &Process::ProcessModel::outletsChanged,
this,
154 &Executor::recompute_ports);
157 node->audio_configuration_changed(st);
159 m_oldInlets = element.inlets();
160 m_oldOutlets = element.outlets();
176 using Gfx::gfx_exec_node::gfx_exec_node;
177 std::string label()
const noexcept
override
179 return std::string(avnd::get_name<Node>());
183 auto node = std::make_shared<named_exec_node>(gfx_exec);
184 node->prepare(*ctx.execState);
190 for(
auto& ctl : element.inlets())
192 if(
auto ctrl = qobject_cast<Process::ControlInlet*>(ctl))
194 auto& p = node->add_control();
195 p->value = ctrl->value();
199 ctrl, &Process::ControlInlet::valueChanged,
this,
203 else if(
auto ctrl = qobject_cast<Process::ValueInlet*>(ctl))
205 auto& p = node->add_control();
209 else if(
auto ctrl = qobject_cast<Process::AudioInlet*>(ctl))
213 else if(
auto ctrl = qobject_cast<Gfx::TextureInlet*>(ctl))
220 for(
auto* outlet : element.outlets())
222 if(
auto ctrl = qobject_cast<Process::ControlOutlet*>(outlet))
224 node->add_control_out();
226 else if(
auto ctrl = qobject_cast<Process::ValueOutlet*>(outlet))
228 node->add_control_out();
230 else if(
auto out = qobject_cast<Gfx::TextureOutlet*>(outlet))
232 node->add_texture_out();
233 out->nodeId = node_id;
238 std::weak_ptr qex_ptr = std::shared_ptr<Execution::ExecutionCommandQueue>(
239 ctx.alias.lock(), &ctx.executionQueue);
240 std::unique_ptr<score::gfx::Node> ptr;
241 if constexpr(GpuGraphicsNode2<Node>)
243 auto gpu_node =
new CustomGpuNode<Node>(qex_ptr, node->control_outs,
id, ctx.doc);
246 else if constexpr(GpuComputeNode2<Node>)
248 auto gpu_node =
new GpuComputeNode<Node>(qex_ptr, node->control_outs,
id, ctx.doc);
251 else if constexpr(GpuNode<Node>)
254 =
new GfxNode<Node>(element, qex_ptr, node->control_outs,
id, ctx.doc);
257 node->id = gfx_exec.ui->register_node(std::move(ptr));
262 void recompute_ports()
265 auto n = std::dynamic_pointer_cast<safe_node<Node>>(this->node);
270 this->in_exec([dp = this->process().dynamic_ports, node = n] {
271 node->dynamic_ports = dp;
272 node->root_inputs().clear();
273 node->root_outputs().clear();
274 node->initialize_all_ports();
278 void connect_controls(
280 std::shared_ptr<safe_node<Node>>& ptr)
282 using dynamic_ports_port_type = avnd::dynamic_ports_input_introspection<Node>;
283 using control_inputs_type = avnd::control_input_introspection<Node>;
284 using curve_inputs_type = avnd::curve_input_introspection<Node>;
285 using soundfile_inputs_type = avnd::soundfile_input_introspection<Node>;
286 using midifile_inputs_type = avnd::midifile_input_introspection<Node>;
287 using raw_file_inputs_type = avnd::raw_file_input_introspection<Node>;
288 using control_outputs_type = avnd::control_output_introspection<Node>;
291 safe_node<Node>& node = *ptr;
292 avnd::effect_container<Node>& eff = node.impl;
297 if constexpr(dynamic_ports_port_type::size > 0)
299 for(
auto state : eff.full_state())
301 dynamic_ports_port_type::for_all_n2(
305 if constexpr(control_inputs_type::size > 0)
307 for(
auto state : eff.full_state())
309 control_inputs_type::for_all_n2(
313 if constexpr(curve_inputs_type::size > 0)
315 for(
auto state : eff.full_state())
317 curve_inputs_type::for_all_n2(
321 if constexpr(soundfile_inputs_type::size > 0)
323 soundfile_inputs_type::for_all_n2(
324 avnd::get_inputs<Node>(eff),
327 setup_soundfile_task_pool(element, ctx, ptr);
329 if constexpr(midifile_inputs_type::size > 0)
331 midifile_inputs_type::for_all_n2(
332 avnd::get_inputs<Node>(eff),
335 if constexpr(raw_file_inputs_type::size > 0)
337 raw_file_inputs_type::for_all_n2(
338 avnd::get_inputs<Node>(eff),
343 if constexpr(control_inputs_type::size > 0 || control_outputs_type::size > 0)
346 std::weak_ptr<safe_node<Node>> weak_node = ptr;
350 con(ctx.doc.coarseUpdateTimer, &QTimer::timeout,
this,
351 [timer_action = std::move(timer_action)] { timer_action(); },
352 Qt::QueuedConnection);
356 void setup_soundfile_task_pool(
358 std::shared_ptr<safe_node<Node>>& ptr)
360 safe_node<Node>& node = *ptr;
361 avnd::effect_container<Node>& eff = node.impl;
363 using soundfile_inputs_type = avnd::soundfile_input_introspection<Node>;
365 auto& tq = score::TaskPool::instance();
366 node.soundfiles.load_request
367 = [&tq, p = std::weak_ptr{ptr}, &ctx](std::string& str,
int idx) {
368 auto eff_ptr = p.lock();
371 tq.post([eff_ptr = std::move(eff_ptr), filename = str, &ctx, idx]()
mutable {
372 if(
auto file = loadSoundfile(filename, ctx.doc, ctx.execState))
374 ctx.executionQueue.enqueue(
375 [sf = std::move(file), p = std::weak_ptr{eff_ptr}, idx]()
mutable {
376 auto eff_ptr = p.lock();
380 avnd::effect_container<Node>& eff = eff_ptr->impl;
381 soundfile_inputs_type::for_nth_mapped_n2(
382 avnd::get_inputs<Node>(eff), idx,
383 [&]<std::size_t NField, std::size_t N>(
384 auto& field, avnd::predicate_index<N> p,
385 avnd::field_index<NField> f) {
386 eff_ptr->soundfile_loaded(sf, p, f);
394 void connect_message_bus(
398 if constexpr(avnd::has_gui_to_processor_bus<Node>)
400 element.from_ui = [p = QPointer{
this}, &eff](QByteArray b) {
404 p->in_exec([mess = std::move(b), &eff]()
mutable {
405 using refl = avnd::function_reflection<&Node::process_message>;
406 static_assert(refl::count <= 1);
408 if constexpr(refl::count == 0)
411 eff.process_message();
413 else if constexpr(refl::count == 1)
415 using arg_type = avnd::first_argument<&Node::process_message>;
416 std::decay_t<arg_type> arg;
419 eff.process_message(std::move(arg));
425 if constexpr(avnd::has_processor_to_gui_bus<Node>)
427 eff.send_message = [
self = QPointer{
this}]<
typename T>(T&& b)
mutable {
431 sizeof(QPointer<QObject>) +
sizeof(b)
432 < Execution::ExecutionCommand::max_storage)
435 [proc = QPointer{&
self->process()}, bb = std::move(b)]()
mutable {
443 [proc = QPointer{&
self->process()},
444 bb = std::make_unique<std::decay_t<T>>(std::move(b))]()
mutable {
453 void connect_worker(const ::Execution::Context& ctx, avnd::effect_container<Node>& eff)
455 if constexpr(avnd::has_worker<Node>)
458 auto& tq = score::TaskPool::instance();
459 using worker_type = decltype(eff.effect.worker);
460 for(
auto& eff : eff.effects())
462 std::weak_ptr eff_ptr = std::shared_ptr<Node>(this->node, &eff);
463 std::weak_ptr qex_ptr = std::shared_ptr<Execution::ExecutionCommandQueue>(
464 ctx.alias.lock(), &ctx.executionQueue);
467 = [&tq, qex_ptr = std::move(qex_ptr),
468 eff_ptr = std::move(eff_ptr)]<
typename... Args>(Args&&... f)
mutable {
471 tq.post([eff_ptr, qex_ptr, ... ff = std::forward<Args>(f)]()
mutable {
479 = decltype(worker_type::work(std::forward<decltype(ff)>(ff)...));
480 if constexpr(std::is_void_v<type_of_result>)
482 worker_type::work(std::forward<decltype(ff)>(ff)...);
488 auto res = worker_type::work(std::forward<decltype(ff)>(ff)...);
494 ossia::qt::run_async(
495 qApp, [eff_ptr = std::move(eff_ptr), qex_ptr = std::move(qex_ptr),
496 res = std::move(res)]()
mutable {
498 std::shared_ptr qex = qex_ptr.lock();
503 [eff_ptr = std::move(eff_ptr), res = std::move(res)]()
mutable {
507 if(
auto p = eff_ptr.lock())
519 void update_controls(std::shared_ptr<safe_node<Node>>& ptr)
521 avnd::effect_container<Node>& eff = ptr->impl;
523 for(
auto state : eff.full_state())
525 avnd::input_introspection<Node>::for_all(
526 state.inputs, [&](
auto& field) { if_possible(field.update(state.effect)); });
531 void cleanup()
override
533 if constexpr(requires { this->process().from_ui; })
535 this->process().from_ui = [](QByteArray arr) {};
540 if constexpr(is_gpu<Node>)
543 auto& gfx_exec = this->system().doc.template plugin<Gfx::DocumentPlugin>().exec;
545 gfx_exec.ui->unregister_node(node_id);
549 for(
auto* outlet : this->process().outlets())
551 if(
auto out = qobject_cast<Gfx::TextureOutlet*>(outlet))
557 ::Execution::ProcessComponent::cleanup();
Definition: GfxApplicationPlugin.hpp:13
Definition: GfxExecNode.hpp:40
Definition: UuidKey.hpp:343
Definition: score-plugin-avnd/Crousti/Executor.hpp:55
Definition: score-plugin-avnd/Crousti/Executor.hpp:74
Definition: score-plugin-avnd/Crousti/ProcessModel.hpp:77
Definition: Factories.hpp:19
Definition: Process/Execution/ProcessComponent.hpp:89
Definition: ExecutionTransaction.hpp:18
Definition: GfxExecNode.hpp:117
Definition: PortForward.hpp:23
Definition: PortForward.hpp:27
Definition: MessageBus.hpp:133
Definition: MessageBus.hpp:37
Definition: ExecutorPortSetup.hpp:337
Definition: ExecutorUpdateControlValueInUi.hpp:76
Definition: ObjectPath.hpp:186