Loading...
Searching...
No Matches
IntervalExecutionHelpers.hpp
1#pragma once
2#include <Process/Dataflow/Port.hpp>
3#include <Process/ExecutionContext.hpp>
4#include <Process/ExecutionSetup.hpp>
5#include <Process/ExecutionTransaction.hpp>
6#include <Process/Process.hpp>
7
8#include <Curve/CurveConversion.hpp>
9
10#include <Scenario/Document/Interval/IntervalModel.hpp>
11#include <Scenario/Document/ScenarioDocument/ScenarioDocumentModel.hpp>
12#include <Scenario/Document/Tempo/TempoProcess.hpp>
13
14#include <ossia/dataflow/graph/graph_interface.hpp>
15#include <ossia/dataflow/graph_edge.hpp>
16#include <ossia/dataflow/graph_node.hpp>
17#include <ossia/dataflow/port.hpp>
18#include <ossia/detail/pod_vector.hpp>
19#include <ossia/editor/curve/curve.hpp>
20#include <ossia/editor/scenario/time_interval.hpp>
21#include <ossia/editor/scenario/time_process.hpp>
22
23namespace Execution
24{
25
26inline std::pair<std::optional<ossia::tempo_curve>, Scenario::TempoProcess*>
27tempoCurve(const Scenario::IntervalModel& itv, const Execution::Context& ctx)
28{
29 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
30 // TODO
31 if(auto proc = itv.tempoCurve())
32 {
33 auto& curve = proc->curve();
34 // TODO recompute whenever tempo changes
35 const auto defaultdur = itv.duration.defaultDuration().msec();
36 auto scale_x = [&ctx, defaultdur](double val) -> int64_t {
37 return ctx.time(TimeVal::fromMsecs(val * defaultdur)).impl;
38 };
39 auto scale_y = [=](double val) -> double {
40 using namespace Scenario;
41 return val * (TempoProcess::max - TempoProcess::min) + TempoProcess::min;
42 };
43
44 ossia::tempo_curve t;
45
46 auto segt_data = curve.sortedSegments();
47 if(segt_data.size() != 0)
48 {
49 t = std::move(*Engine::score_to_ossia::curve<int64_t, double>(
50 scale_x, scale_y, segt_data, {}));
51 }
52
53 return {std::optional<ossia::tempo_curve>{std::move(t)}, proc};
54 }
55 else
56 {
57 return {};
58 }
59}
60inline ossia::time_signature_map
61timeSignatureMap(const Scenario::IntervalModel& itv, const Execution::Context& ctx)
62{
63 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
64 ossia::time_signature_map ret;
65 for(const auto& [time, sig] : itv.timeSignatureMap())
66 {
67 ret[ctx.time(time)] = sig;
68 }
69 return ret;
70}
71
72inline auto propagatedOutlets(const Process::Outlets& outlets) noexcept
73{
74 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
75 ossia::pod_vector<std::size_t> propagated_outlets;
76 for(std::size_t i = 0; i < outlets.size(); i++)
77 {
78 if(auto o = qobject_cast<Process::AudioOutlet*>(outlets[i]))
79 if(o->propagate())
80 propagated_outlets.push_back(i);
81 }
82 return propagated_outlets;
83}
84
85inline void connectPropagated(
86 const ossia::node_ptr& process_node, const ossia::node_ptr& interval_node,
87 ossia::graph_interface& g,
88 const ossia::pod_vector<std::size_t>& propagated_outlets) noexcept
89{
90 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
91 const auto& outs = process_node->root_outputs();
92 for(std::size_t propagated : propagated_outlets)
93 {
94 if(propagated >= outs.size())
95 continue;
96
97 if(outs[propagated]->which() == ossia::audio_port::which)
98 {
99 auto cable = g.allocate_edge(
100 ossia::immediate_glutton_connection{}, outs[propagated],
101 interval_node->root_inputs()[0], process_node, interval_node);
102 g.connect(cable);
103 }
104 }
105}
106
107inline void updatePropagated(
108 const ossia::node_ptr& process_node, const ossia::node_ptr& interval_node,
109 ossia::graph_interface& g, std::size_t port_idx, bool is_propagated) noexcept
110{
111 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
112 const auto& outs = process_node->root_outputs();
113
114 if(port_idx >= outs.size())
115 return;
116
117 const ossia::outlet& outlet = *outs[port_idx];
118
119 if(!outlet.target<ossia::audio_port>())
120 return;
121
122 // Remove cables if depropagated, add cables if repropagated
123 if(is_propagated)
124 {
125 for(const ossia::graph_edge* edge : outlet.targets)
126 {
127 if(edge->in_node == interval_node)
128 return;
129 }
130
131 auto cable = g.allocate_edge(
132 ossia::immediate_glutton_connection{}, outs[port_idx],
133 interval_node->root_inputs()[0], process_node, interval_node);
134 g.connect(cable);
135 }
136 else
137 {
138 for(ossia::graph_edge* edge : outlet.targets)
139 {
140 if(edge->in_node == interval_node)
141 {
142 g.disconnect(edge);
143 return;
144 }
145 }
146 }
147}
148
150{
151 // Note : this looks a bit wtf, it is to handle RawPtrExecution
152 std::shared_ptr<ossia::time_interval> cst;
153 ossia::time_interval* cst_ptr{};
154 std::weak_ptr<ossia::time_process> oproc_weak;
155 std::weak_ptr<ossia::graph_interface> g_weak;
156 ossia::pod_vector<std::size_t> propagated_outlets;
157
158 void operator()() const noexcept
159 {
160 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
161 auto oproc = oproc_weak.lock();
162 if(!oproc)
163 return;
164
165 auto g = g_weak.lock();
166 if(!g)
167 return;
168
169 cst_ptr->add_time_process(oproc);
170 if(!oproc->node)
171 return;
172
173 connectPropagated(oproc->node, cst_ptr->node, *g, propagated_outlets);
174 }
175};
176
178{
179 const Execution::Context& system;
181 std::weak_ptr<ossia::graph_node> cst_node_weak;
182 std::weak_ptr<ossia::time_process> oproc_weak;
183 std::weak_ptr<ossia::graph_interface> g_weak;
184 Process::Outlet* outlet{};
185
186 void operator()(bool propagate) const noexcept
187 {
188 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
189
190 // TODO find a better way !
191 auto port_index
192 = std::distance(proc.outlets().begin(), ossia::find(proc.outlets(), outlet));
193
194 system.executionQueue.enqueue(
195 [cst_node_weak = this->cst_node_weak, g_weak = this->g_weak,
196 oproc_weak = this->oproc_weak, port_index, propagate] {
197 const auto g = g_weak.lock();
198 if(!g)
199 return;
200
201 const auto cst_node = cst_node_weak.lock();
202 if(!cst_node)
203 return;
204
205 const auto oproc = oproc_weak.lock();
206 if(!oproc)
207 return;
208
209 const auto& proc_node = oproc->node;
210 if(!oproc->node)
211 return;
212
213 updatePropagated(proc_node, cst_node, *g, port_index, propagate);
214 });
215 }
216};
217
218template <typename T>
220{
221 T& component;
222 std::weak_ptr<ossia::graph_node> fw_node;
223
225 std::weak_ptr<ossia::time_process> oproc_weak;
226
227 std::weak_ptr<ossia::graph_interface> g_weak;
228
229 void operator()() const noexcept
230 {
231 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
232 for(Process::Outlet* outlet : proc.outlets())
233 {
234 if(auto o = qobject_cast<Process::AudioOutlet*>(outlet))
235 {
236 QObject::disconnect(
237 o, &Process::AudioOutlet::propagateChanged, &component, nullptr);
238 QObject::connect(
239 o, &Process::AudioOutlet::propagateChanged, &component,
241 component.system(), proc, fw_node, oproc_weak, g_weak, outlet});
242 }
243 }
244 }
245};
246
253{
254 std::weak_ptr<ossia::graph_node> cst_node_weak;
255 std::weak_ptr<ossia::time_process> oproc_weak;
256 std::weak_ptr<ossia::graph_interface> g_weak;
258
259 void operator()(Execution::Transaction* commands) const noexcept
260 {
261 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
262 commands->push_back([cst_node_weak = this->cst_node_weak,
263 oproc_weak = this->oproc_weak, g_weak = this->g_weak,
264 propagated = propagatedOutlets(proc.outlets())] {
265 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
266 auto cst_node = cst_node_weak.lock();
267 if(!cst_node)
268 return;
269 auto g = g_weak.lock();
270 if(!g)
271 return;
272 auto oproc = oproc_weak.lock();
273 if(!oproc || !oproc->node)
274 return;
275
276 const std::size_t n = oproc->node->root_outputs().size();
277 for(std::size_t i = 0; i < n; i++)
278 updatePropagated(oproc->node, cst_node, *g, i, ossia::contains(propagated, i));
279 });
280 }
281};
282
284{
285 std::weak_ptr<ossia::graph_node> cst_node_weak;
286 std::weak_ptr<ossia::time_process> oproc_weak;
287 std::weak_ptr<ossia::time_interval> itv_weak;
288 std::weak_ptr<ossia::graph_interface> g_weak;
290
291 void operator()(
292 const ossia::node_ptr& old_node, const ossia::node_ptr& new_node,
293 Execution::Transaction* commands) const noexcept
294 {
295 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
296
297 commands->push_back([cst_node_weak = this->cst_node_weak,
298 oproc_weak = this->oproc_weak,
299 itv_weak = this->itv_weak, g_weak = this->g_weak,
300 propagated = propagatedOutlets(proc.outlets()), old_node,
301 new_node] {
302 OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
303 auto cst_node = cst_node_weak.lock();
304 if(!cst_node)
305 return;
306 auto g = g_weak.lock();
307 if(!g)
308 return;
309
310 // Re-seek the resampler to the current model time so a file swap
311 // on a running interval lands in phase.
312 if(auto itv = itv_weak.lock())
313 {
314 if(auto oproc = oproc_weak.lock())
315 {
316 const auto date = itv->get_date();
317 if(date.impl != 0)
318 {
319 oproc->transport(date, itv->current_transport_info());
320 }
321 }
322 }
323
324 // In-place reload: skip the rewire to avoid a momentary audio break.
325 if(old_node == new_node)
326 return;
327
328 // Remove propagate edges from the old node
329 if(old_node)
330 {
331 ossia::graph_node& n = *old_node;
332 for(auto& outlet : n.root_outputs())
333 {
334 auto targets = outlet->targets;
335 for(auto e : targets)
336 {
337 if(e->in_node.get() == cst_node.get())
338 {
339 g->disconnect(e);
340 }
341 }
342 }
343 }
344
345 // Add edges to the new node
346 if(new_node)
347 {
348 connectPropagated(new_node, cst_node, *g, propagated);
349 }
350 });
351 }
352};
353
354}
Definition Port.hpp:300
The Process class.
Definition score-lib-process/Process/Process.hpp:75
Definition IntervalModel.hpp:50
Components used for the execution of a score.
Definition ProcessComponent.cpp:14
Main plug-in of score.
Definition score-plugin-dataflow/Dataflow/PortItem.hpp:13
Definition IntervalExecutionHelpers.hpp:150
Definition ExecutionContext.hpp:196
ExecutionCommandQueue & executionQueue
Definition ExecutionContext.hpp:242
Definition IntervalExecutionHelpers.hpp:284
Definition IntervalExecutionHelpers.hpp:253
Definition IntervalExecutionHelpers.hpp:178
Definition IntervalExecutionHelpers.hpp:220
Definition ExecutionTransaction.hpp:21
Definition PortForward.hpp:27