Loading...
Searching...
No Matches
ExecutorPortSetup.hpp
1#pragma once
2#include <Process/Process.hpp>
3
4#include <Crousti/File.hpp>
5#include <Crousti/ProcessModel.hpp>
6
7#include <avnd/binding/ossia/node.hpp>
8
9namespace oscr
10{
11
12template <typename Node, typename Field, std::size_t NPred, std::size_t NField>
14{
15 using ExecNode = safe_node<Node>;
16 const Execution::Context& ctx;
17 std::weak_ptr<ExecNode> weak_node;
18 Field& field;
19 void operator()(const ossia::value& val)
20 {
21 using control_value_type = std::decay_t<decltype(Field::value)>;
22
23 if(auto node = weak_node.lock())
24 {
25 control_value_type v;
26 node->from_ossia_value(field, val, v, avnd::field_index<NField>{});
27 ctx.executionQueue.enqueue([weak_node = weak_node, v = std::move(v)]() mutable {
28 if(auto n = weak_node.lock())
29 {
30 n->template control_updated_from_ui<control_value_type, NPred>(std::move(v));
31 }
32 });
33 }
34 }
35};
36
37template <typename Node, typename Field, std::size_t NPred, std::size_t NField>
39{
40 using ExecNode = safe_node<Node>;
41 const Execution::Context& ctx;
42 std::weak_ptr<ExecNode> weak_node;
43 Field& field;
44 int port_index;
45 void operator()(const ossia::value& val)
46 {
47 using control_value_type = std::decay_t<decltype(Field::value)>;
48
49 if(auto node = weak_node.lock())
50 {
51 control_value_type v;
52 node->from_ossia_value(field, val, v, avnd::field_index<NField>{});
53 ctx.executionQueue.enqueue(
54 [weak_node = weak_node, port_index = port_index, v = std::move(v)]() mutable {
55 if(auto n = weak_node.lock())
56 {
57 n->template control_updated_from_ui<control_value_type, NPred>(
58 std::move(v), port_index);
59 }
60 });
61 }
62 }
63};
64
65template <typename Node, typename Field>
67{
68 using ExecNode = safe_node<Node>;
70
71 Model& element;
72 const Execution::Context& ctx;
73 const std::shared_ptr<ExecNode>& node_ptr;
74 QObject* parent;
75
76 void invoke_update(Field& param, int k)
77 {
78 avnd::effect_container<Node>& eff = node_ptr->impl;
79 {
80 for(auto state : eff.full_state())
81 {
82 if constexpr(avnd::dynamic_ports_port<Field>)
83 {
84 if_possible(param.ports[k].update(state.effect));
85 }
86 else
87 {
88 if_possible(param.update(state.effect));
89 }
90 }
91 }
92 }
93};
94template <typename Node, typename Field, std::size_t N, std::size_t NField>
96
97template <typename Node, typename Field, std::size_t N, std::size_t NField>
99{
100 using ExecNode = safe_node<Node>;
102
103 void initialize_control(Field& param, Process::ControlInlet* inlet, int k)
104 {
105 // Initialize the control with the current value of the inlet if it is not an optional
106 if constexpr(avnd::dynamic_ports_port<Field>)
107 {
108 if constexpr(!requires { param.ports[0].value.reset(); })
109 {
110 this->node_ptr->from_ossia_value(
111 param, inlet->value(), param.ports[k].value, avnd::field_index<NField>{});
112 }
113 }
114 else
115 {
116 if constexpr(!requires { param.value.reset(); })
117 {
118 this->node_ptr->from_ossia_value(
119 param, inlet->value(), param.value, avnd::field_index<NField>{});
120 }
121 }
122 }
123
124 void update_controller(Field& param, Process::ControlInlet* inlet)
125 {
126 // FIXME proper tag
127 if constexpr(requires { param.update_controller; })
128 {
129 param.update_controller
130 = [inlet = QPointer{inlet}, self = QPointer{&this->element}](auto&& value) {
131 if(!self || !inlet)
132 return;
133
134 // Notify the UI if the object has the power
135 // to actually change the value of the control
136 ossia::qt::run_async(qApp, [self, inlet, val = std::move(value)] {
137 if(!self || !inlet)
138 return;
139
140 // FIXME better to use in_edit queue ?
141 // FIXME not too efficient but which choice do we have ?
142 static const Field field;
143 oscr::to_ossia_value(field, val);
144 inlet->setValue(val);
145 });
146 };
147 }
148 }
149
150 void connect_control_to_ui(Field& param, Process::ControlInlet* inlet, int k)
151 {
152 // Connect to changes
153 std::weak_ptr<ExecNode> weak_node = this->node_ptr;
154 if constexpr(avnd::dynamic_ports_port<Field>)
155 {
156 using port_type = avnd::dynamic_port_type<Field>;
157 QObject::connect(
158 inlet, &Process::ControlInlet::valueChanged, this->parent,
160 this->ctx, weak_node, param.ports[k], k});
161
162 this->update_controller(param, inlet);
163 }
164 else
165 {
166 QObject::connect(
167 inlet, &Process::ControlInlet::valueChanged, this->parent,
168 con_unvalidated<Node, Field, N, NField>{this->ctx, weak_node, param});
169
170 this->update_controller(param, inlet);
171 }
172 }
173};
174
175template <typename Node, avnd::soundfile_port Field, std::size_t N, std::size_t NField>
176struct setup_control_for_exec<Node, Field, N, NField>
177 : setup_control_for_exec_base<Node, Field>
178{
179 using ExecNode = safe_node<Node>;
181
182 void initialize_control(Field& param, Process::ControlInlet* inlet, int k)
183 {
184 // FIXME handle dynamic ports correctly
185 // First we can load it directly since execution hasn't started yet
186 if(auto hdl = loadSoundfile(inlet->value(), this->ctx.doc, this->ctx.execState))
187 this->node_ptr->soundfile_loaded(
188 hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
189 }
190
191 void connect_control_to_ui(Field& param, Process::ControlInlet* inlet, int k)
192 {
193 // Connect to changes
194 std::weak_ptr<ExecNode> weak_node = this->node_ptr;
195 std::weak_ptr<ossia::execution_state> weak_st = this->ctx.execState;
196 QObject::connect(
197 inlet, &Process::ControlInlet::valueChanged, this->parent,
198 [&ctx = this->ctx, weak_node = std::move(weak_node),
199 weak_st = std::move(weak_st)](const ossia::value& v) {
200 if(auto n = weak_node.lock())
201 if(auto st = weak_st.lock())
202 if(auto file = loadSoundfile(v, ctx.doc, st))
203 {
204 ctx.executionQueue.enqueue([f = std::move(file), weak_node]() mutable {
205 auto n = weak_node.lock();
206 if(!n)
207 return;
208
209 // We store the sound file handle returned in this lambda so that it gets
210 // GC'd in the main thread
211 n->soundfile_loaded(
212 f, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
213 });
214 }
215 });
216 }
217};
218
219template <typename Node, avnd::midifile_port Field, std::size_t N, std::size_t NField>
220struct setup_control_for_exec<Node, Field, N, NField>
221 : setup_control_for_exec_base<Node, Field>
222{
223 using ExecNode = safe_node<Node>;
224 using Model = ProcessModel<Node>;
225
226 void initialize_control(Field& param, Process::ControlInlet* inlet, int k)
227 {
228 // FIXME handle dynamic ports correctly
229
230 // First we can load it directly since execution hasn't started yet
231 if(auto hdl = loadMidifile(inlet->value(), this->ctx.doc))
232 this->node_ptr->midifile_loaded(
233 hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
234 }
235
236 void connect_control_to_ui(Field& param, Process::ControlInlet* inlet, int k)
237 {
238 // Connect to changes
239 std::weak_ptr<ExecNode> weak_node = this->node_ptr;
240 std::weak_ptr<ossia::execution_state> weak_st = this->ctx.execState;
241 QObject::connect(
242 inlet, &Process::ControlInlet::valueChanged, this->parent,
243 [inlet, &ctx = this->ctx,
244 weak_node = std::move(weak_node)](const ossia::value& v) {
245 if(auto n = weak_node.lock())
246 if(auto file = loadMidifile(v, ctx.doc))
247 {
248 ctx.executionQueue.enqueue([f = std::move(file), weak_node]() mutable {
249 auto n = weak_node.lock();
250 if(!n)
251 return;
252
253 // We store the sound file handle returned in this lambda so that it gets
254 // GC'd in the main thread
255 n->midifile_loaded(
256 f, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
257 });
258 }
259 });
260 }
261};
262
263template <typename Node, avnd::raw_file_port Field, std::size_t N, std::size_t NField>
264struct setup_control_for_exec<Node, Field, N, NField>
265 : setup_control_for_exec_base<Node, Field>
266{
267 using ExecNode = safe_node<Node>;
268 using Model = ProcessModel<Node>;
269
270 static constexpr bool has_text = requires { decltype(Field::file)::text; };
271 static constexpr bool has_mmap = requires { decltype(Field::file)::mmap; };
272
273 void initialize_control(Field& param, Process::ControlInlet* inlet, int k)
274 {
275 // FIXME handle dynamic ports correctly
276
277 // First we can load it directly since execution hasn't started yet
278 if(auto hdl = loadRawfile(inlet->value(), this->ctx.doc, has_text, has_mmap))
279 {
280 if constexpr(avnd::port_can_process<Field>)
281 {
282 // FIXME also do it when we get a run-time message from the exec engine,
283 // OSC, etc
284 auto func = executePortPreprocess<Field>(*hdl);
285 this->node_ptr->file_loaded(
286 hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
287 if(func)
288 func(this->node_ptr->impl.effect);
289 }
290 else
291 {
292 this->node_ptr->file_loaded(
293 hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
294 }
295 }
296 }
297
298 void connect_control_to_ui(Field& param, Process::ControlInlet* inlet, int k)
299 {
300 // Connect to changes
301 std::weak_ptr<ExecNode> weak_node = this->node_ptr;
302 std::weak_ptr<ossia::execution_state> weak_st = this->ctx.execState;
303 QObject::connect(
304 inlet, &Process::ControlInlet::valueChanged, this->parent,
305 [inlet, &ctx = this->ctx, weak_node = std::move(weak_node)] {
306 if(auto n = weak_node.lock())
307 if(auto file = loadRawfile(inlet->value(), ctx.doc, has_text, has_mmap))
308 {
309 if constexpr(avnd::port_can_process<Field>)
310 {
311 auto func = executePortPreprocess<Field>(*file);
312 ctx.executionQueue.enqueue(
313 [f = std::move(file), weak_node, ff = std::move(func)]() mutable {
314 auto n = weak_node.lock();
315 if(!n)
316 return;
317
318 // We store the sound file handle returned in this lambda so that it gets
319 // GC'd in the main thread
320 n->file_loaded(f, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
321 if(ff)
322 ff(n->impl.effect);
323 });
324 }
325 else
326 {
327 ctx.executionQueue.enqueue([f = std::move(file), weak_node]() mutable {
328 auto n = weak_node.lock();
329 if(!n)
330 return;
331
332 // We store the sound file handle returned in this lambda so that it gets
333 // GC'd in the main thread
334 n->file_loaded(f, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
335 });
336 }
337 }
338 });
339 }
340};
341
342template <typename Node>
344{
345 using ExecNode = safe_node<Node>;
347
348 Model& element;
349 const Execution::Context& ctx;
350 const std::shared_ptr<ExecNode>& node_ptr;
351 QObject* parent;
352
353 // Main function being invoked, which dispatches to all the actual implementations
354 template <typename Field, std::size_t N, std::size_t NField>
355 constexpr void
356 operator()(Field& param, avnd::predicate_index<N> np, avnd::field_index<NField> nf)
357 {
358 const auto ports = element.avnd_input_idx_to_model_ports(NField);
359
360 if constexpr(avnd::dynamic_ports_port<Field>)
361 {
362 param.ports.resize(ports.size());
363 }
364
365 int k = 0;
366 for(auto p : ports)
367 {
368 if(auto inlet = qobject_cast<Process::ControlInlet*>(p))
369 {
371 element, ctx, node_ptr, parent};
372
373 setup.initialize_control(param, inlet, k);
374
375 setup.invoke_update(param, k);
376
377 setup.connect_control_to_ui(param, inlet, k);
378 }
379 k++;
380 }
381 // Else it's an unhandled value inlet
382 }
383};
384
385}
Definition Port.hpp:203
Definition score-plugin-avnd/Crousti/ProcessModel.hpp:79
TreeNode< DeviceExplorerNode > Node
Definition DeviceNode.hpp:74
Definition Factories.hpp:19
Definition ExecutionContext.hpp:76
ExecutionCommandQueue & executionQueue
Definition ExecutionContext.hpp:91
Definition ExecutorPortSetup.hpp:39
Definition ExecutorPortSetup.hpp:14
Definition ExecutorPortSetup.hpp:344
Definition ExecutorPortSetup.hpp:67
Definition ExecutorPortSetup.hpp:99