• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 38402803580

13 Oct 2026 01:09PM UTC coverage: 48.861%. First build
38402803580

Pull #2326

github

web-flow
Merge a12afe4e9 into 4b2f7f45e
Pull Request #2326: gfx: engine fixes for the shading tiers, scene lights and blending, glTF material parameters

1785 of 2258 new or added lines in 73 files covered. (79.05%)

160466 of 328410 relevant lines covered (48.86%)

146407.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.71
/src/plugins/score-lib-process/Process/ExecutionSetup.cpp
1
#include <State/Address.hpp>
2

3
#include <Process/Dataflow/Cable.hpp>
4
#include <Process/Dataflow/Port.hpp>
5
#include <Process/ExecutionContext.hpp>
6
#include <Process/ExecutionFunctions.hpp>
7
#include <Process/ExecutionSetup.hpp>
8
#include <Process/ExecutionTransaction.hpp>
9
#include <Process/Process.hpp>
10

11
#include <LocalTree/ScriptableProcessComponent.hpp>
12

13
#include <QPointer>
14
#include <QTimer>
15

16
#include <ossia/dataflow/execution_state.hpp>
17
#include <ossia/dataflow/for_each_port.hpp>
18
#include <ossia/dataflow/graph/graph_interface.hpp>
19
#include <ossia/dataflow/graph_edge.hpp>
20
#include <ossia/dataflow/graph_node.hpp>
21
#include <ossia/dataflow/port.hpp>
22
#include <ossia/editor/scenario/time_process.hpp>
23
#include <ossia/network/common/destination_qualifiers.hpp>
24

25
namespace Execution
26
{
27

28
namespace
29
{
30
struct ContextEnqueuer
31
{
32
  SetupContext& self;
33
  template <typename F>
34
  void operator()(F&& f)
3,505 ✔
35
  {
36
    static_assert(std::is_nothrow_move_constructible_v<F>);
37
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
3,505 ✔
38
    self.context.executionQueue.enqueue(std::move(f));
3,505 ✔
39
  }
3,505 ✔
40
};
41
struct VectorEnqueuer
42
{
43
  Transaction& vec;
44
  template <typename F>
45
  void operator()(F&& f)
1,824 ✔
46
  {
47
    static_assert(std::is_nothrow_move_constructible_v<F>);
48
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
1,824 ✔
49
    vec.push_back(std::move(f));
1,824 ✔
50
  }
1,824 ✔
51
};
52

53
// Changed from lambda to explicit struct to improve debuggability
54
static auto enqueue_in_context(SetupContext& self) noexcept
1,097 ✔
55
{
56
  return ContextEnqueuer{self};
1,097 ✔
57
}
58

59
static auto enqueue_in_vector(Transaction& vec) noexcept
1,050 ✔
60
{
61
  return VectorEnqueuer{vec};
1,050 ✔
62
}
63
}
64
ossia::net::node_base*
65
findNode(const ossia::execution_state& st, const State::Address& addr)
3,578 ✔
66
{
67
  auto& devs = st.edit_devices();
3,578 ✔
68
  auto dev_p = ossia::find_if(
3,578 ✔
69
      devs, [d = addr.device.toStdString()](auto& dev) { return dev->get_name() == d; });
7,662 ✔
70
  if(dev_p == devs.end())
3,578 ✔
71
    return nullptr;
17 ✔
72
  return ossia::net::find_node(
3,561 ✔
73
      (*dev_p)->get_root_node(), addr.path.join("/").toStdString());
3,561 ✔
74
}
3,578 ✔
75

76
std::optional<ossia::destination> makeDestination(
25 ✔
77
    const ossia::execution_state& devices, const State::AddressAccessor& addr)
78
{
79
  auto n = findNode(devices, addr.address);
25 ✔
80
  if(!n)
25 ✔
81
    return {};
13 ✔
82

83
  auto p = n->get_parameter();
12 ✔
84
  if(!p)
12 ✔
85
    return {};
×
86

87
  auto& qual = addr.qualifiers.get();
12 ✔
88
  return ossia::destination{*p, qual.accessors, qual.unit};
12 ✔
89
}
25 ✔
90

91
template <typename Impl>
92
void SetupContext::disconnect_cable_impl(const Process::Cable& c, Impl&& impl)
103 ✔
93
{
94
  if(!context.created) {
103 ✔
95
    return;
82 ✔
96
  }
97
  auto it = m_cables.find(c.id());
21 ✔
98
  if(it != m_cables.end())
21 ✔
99
  {
100
    impl([cable = it->second, graph = context.execGraph] {
41 ✔
101
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
20 ✔
102
      graph->disconnect(cable);
20 ✔
103
    });
20 ✔
104
    // The edge is gone from the graph: a later connectCable() for the same
105
    // id makes a new one, and whoever asks whether the cable is wired up
106
    // (a node rebuilding its ports) must not find this one.
107
    m_cables.erase(it);
21 ✔
108
  }
21 ✔
109
}
103 ✔
110

111
template <typename Impl>
112
void SetupContext::connect_cable_impl(Process::Cable& cable, Impl&& impl)
118 ✔
113
{
114
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
118 ✔
115
  if(!context.created) {
118 ✔
116
    return;
76 ✔
117
  }
118
  ossia::node_ptr source_node{}, sink_node{};
42 ✔
119
  ossia::outlet_ptr source_port{};
42 ✔
120
  ossia::inlet_ptr sink_port{};
42 ✔
121
  if(auto port_src = cable.source().try_find(context.doc))
42 ✔
122
  {
123
    auto it = outlets.find(port_src);
42 ✔
124
    if(it != outlets.end())
42 ✔
125
    {
126
      source_node = it->second.first;
42 ✔
127
      source_port = it->second.second;
42 ✔
128
    }
42 ✔
129
  }
42 ✔
130
  if(auto port_snk = cable.sink().try_find(context.doc))
42 ✔
131
  {
132
    auto it = inlets.find(port_snk);
42 ✔
133
    if(it != inlets.end())
42 ✔
134
    {
135
      sink_node = it->second.first;
42 ✔
136
      sink_port = it->second.second;
42 ✔
137
    }
42 ✔
138
  }
42 ✔
139

140
  if(source_node && sink_node && source_port && sink_port)
42 ✔
141
  {
142
    ossia::edge_ptr edge;
42 ✔
143
    switch(cable.type())
42 ✔
144
    {
145
      case Process::CableType::ImmediateStrict: {
146
        edge = context.execGraph->allocate_edge(
×
147
            ossia::immediate_strict_connection{}, std::move(source_port),
×
148
            std::move(sink_port), std::move(source_node), std::move(sink_node));
149
        break;
150
      }
151
      case Process::CableType::ImmediateGlutton: {
152
        edge = context.execGraph->allocate_edge(
84 ✔
153
            ossia::immediate_glutton_connection{}, std::move(source_port),
42 ✔
154
            std::move(sink_port), std::move(source_node), std::move(sink_node));
42 ✔
155
        break;
42 ✔
156
      }
157
      case Process::CableType::DelayedStrict: {
158
        edge = context.execGraph->allocate_edge(
×
159
            ossia::delayed_strict_connection{}, std::move(source_port),
×
160
            std::move(sink_port), std::move(source_node), std::move(sink_node));
161
        break;
162
      }
163
      case Process::CableType::DelayedGlutton: {
164
        edge = context.execGraph->allocate_edge(
×
165
            ossia::delayed_glutton_connection{}, std::move(source_port),
×
166
            std::move(sink_port), std::move(source_node), std::move(sink_node));
167
        break;
168
      }
169
    }
170

171
    m_cables[cable.id()] = edge;
42 ✔
172
    impl([edge, graph = context.execGraph]() mutable {
84 ✔
173
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
42 ✔
174
      graph->connect(std::move(edge));
42 ✔
175
    });
42 ✔
176
  }
42 ✔
177
}
118 ✔
178

179
// Pushed once, when the node is registered; the address machinery never touches
180
// `type` afterwards.
181
template <typename T, typename Impl>
182
static void set_declared_unit_impl(
4,142 ✔
183
    const Process::Port& proc_port, const T& port, Impl&& append)
184
{
185
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
4,142 ✔
186
  const ossia::unit_t& u = proc_port.unit().get();
4,142 ✔
187
  if(!u)
4,142 ✔
188
    return;
4,125 ✔
189

190
  append([port, u] {
34 ✔
191
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
17 ✔
192
    if(ossia::value_port* dat = port->template target<ossia::value_port>())
17 ✔
193
    {
194
      // A unit the node declared is more precise than the widget's and wins; a
195
      // plain value type does not.
196
      if(!dat->type.target<ossia::unit_t>())
17 ✔
197
        dat->type = u;
×
198
    }
17 ✔
199
  });
17 ✔
200
}
4,142 ✔
201

202
// Without an explicit address, a scriptable port uses its published parameter
203
static State::AddressAccessor effectiveAddress(const Process::Port& port)
4,214 ✔
204
{
205
  if(port.address().isSet())
4,214 ✔
206
    return port.address();
192 ✔
207
  return State::AddressAccessor{LocalTree::scriptableAddress(port)};
4,022 ✔
208
}
4,214 ✔
209

210
template <typename Port_T, typename OssiaPort_T, typename Impl>
211
void SetupContext::bind_address(
4,142 ✔
212
    Port_T& proc_port, const OssiaPort_T& ossia_port,
213
    RegisteredPorts::connections& cons, Impl&& impl)
214
{
215
  for(auto& con : cons)
4,142 ✔
216
    QObject::disconnect(con);
×
217
  cons.clear();
4,142 ✔
218

219
  // The ossia port is looked up when this runs: a queued call can arrive after
220
  // a program change has replaced the one the port was bound with.
221
  auto rebind = [this, port = QPointer<Process::Port>{&proc_port}] {
4,170 ✔
222
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
28 ✔
223
    if(port)
28 ✔
224
      this->rebind(port.data());
27 ✔
225
  };
28 ✔
226
  cons.push_back(connect(&proc_port, &Process::Port::addressChanged, this, rebind));
4,142 ✔
227
  // Queued: the namespace updates the published address on these signals too
228
  cons.push_back(connect(
8,284 ✔
229
      &proc_port, &Process::Port::scriptableChanged, this, rebind, Qt::QueuedConnection));
4,142 ✔
230
  cons.push_back(connect(
8,284 ✔
231
      &proc_port, &Process::Port::exposedChanged, this, rebind, Qt::QueuedConnection));
4,142 ✔
232
  if(auto proc = Process::parentProcess(&proc_port))
4,142 ✔
233
    cons.push_back(connect(
7,092 ✔
234
        proc, &Process::ProcessModel::scriptableChanged, this, rebind,
3,546 ✔
235
        Qt::QueuedConnection));
236
  follow_published();
4,142 ✔
237
  set_declared_unit_impl(proc_port, ossia_port, impl);
4,142 ✔
238

239
  // Also registers the port with the execution state once the address resolves.
240
  set_destination_impl(context, effectiveAddress(proc_port), ossia_port, impl);
4,142 ✔
241
}
4,142 ✔
242

243
template <typename Impl>
244
void SetupContext::register_inlet_impl(
3,285 ✔
245
    Process::Inlet& proc_port, const ossia::inlet_ptr& ossia_port,
246
    const std::shared_ptr<ossia::graph_node>& node, Impl&& impl)
247
{
248
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
3,285 ✔
249
  SCORE_ASSERT(node);
3,285 ✔
250
  SCORE_ASSERT(ossia_port);
3,285 ✔
251

252
  bind_address(proc_port, ossia_port, runtime_connections[node].inlets[proc_port.id()], impl);
3,285 ✔
253

254
  inlets.insert({&proc_port, std::make_pair(node, ossia_port)});
3,285 ✔
255
}
3,285 ✔
256

257
template <typename Impl>
258
void SetupContext::register_node_impl(
1,036 ✔
259
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
260
    const std::shared_ptr<ossia::graph_node>& node, Impl&& exec)
261
{
262
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
1,036 ✔
263
  if(node)
1,036 ✔
264
  {
265
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
1,036 ✔
266
    exec([wg, node = node]() mutable {
2,072 ✔
267
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
1,036 ✔
268
      if(auto g = wg.lock())
2,072 ✔
269
        g->add_node(std::move(node));
1,036 ✔
270
    });
1,036 ✔
271

272
    const std::size_t proc_n_inlets = proc_inlets.size();
1,036 ✔
273
    const std::size_t proc_n_outlets = proc_outlets.size();
1,036 ✔
274

275
    const std::size_t ossia_n_inlets = node->root_inputs().size();
1,036 ✔
276
    const std::size_t ossia_n_outlets = node->root_outputs().size();
1,036 ✔
277

278
    std::size_t n_inlets = std::min(proc_n_inlets, ossia_n_inlets);
1,036 ✔
279
    std::size_t n_outlets = std::min(proc_n_outlets, ossia_n_outlets);
1,036 ✔
280

281
    for(std::size_t i = 0; i < n_inlets; i++)
2,886 ✔
282
    {
283
      register_inlet_impl(*proc_inlets[i], node->root_inputs()[i], node, exec);
1,850 ✔
284
    }
1,850 ✔
285

286
    for(std::size_t i = 0; i < n_outlets; i++)
1,776 ✔
287
    {
288
      register_outlet_impl(*proc_outlets[i], node->root_outputs()[i], node, exec);
740 ✔
289
    }
740 ✔
290
  }
1,036 ✔
291
}
1,036 ✔
292

293
void SetupContext::register_node(
404 ✔
294
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node)
295
{
296
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
404 ✔
297
  register_node(proc.inlets(), proc.outlets(), node);
404 ✔
298
  proc_map[node.get()] = &proc;
404 ✔
299
}
404 ✔
300

301
void SetupContext::unregister_node(
406 ✔
302
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node)
303
{
304
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
406 ✔
305
  unregister_node(proc.inlets(), proc.outlets(), node);
406 ✔
306
  proc_map.erase(node.get());
406 ✔
307
}
406 ✔
308

309
void SetupContext::register_node(
×
310
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node,
311
    Transaction& vec)
312
{
313
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
314
  register_node(proc.inlets(), proc.outlets(), node, vec);
×
315
  proc_map[node.get()] = &proc;
×
316
}
×
317

318
void SetupContext::unregister_node(
×
319
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node,
320
    Transaction& vec)
321
{
322
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
323
  unregister_node(proc.inlets(), proc.outlets(), node, vec);
×
324
  proc_map.erase(node.get());
×
325
}
×
326

327
template <typename T, typename Impl>
328
void set_destination_impl(
4,214 ✔
329
    const Context& plug, const State::AddressAccessor& address, const T& port,
330
    Impl&& append)
331
{
332
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
4,214 ✔
333
  if(plug.retired)
4,214 ✔
NEW
334
    return;
×
335
  auto& s = plug.execState;
4,214 ✔
336
  auto& g = plug.execGraph;
4,214 ✔
337
  if(!g)
4,214 ✔
338
    return;
×
339

340
  if(address.address.device.isEmpty())
4,214 ✔
341
  {
342
    append([ws = std::weak_ptr{s}, port, wg = std::weak_ptr{g}] {
7,574 ✔
343
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
3,785 ✔
344
      auto s = ws.lock();
3,785 ✔
345
      if(!s)
3,785 ✔
346
        return;
×
347
      auto g = wg.lock();
3,785 ✔
348
      if(!g)
3,785 ✔
349
        return;
×
350
      if(port->address)
3,785 ✔
351
      {
352
        s->unregister_port(*port);
7 ✔
353
        port->address = {};
7 ✔
354
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
7 ✔
355
        {
356
          dat->address_unit = {};
7 ✔
357
          dat->index = {};
7 ✔
358
        }
7 ✔
359
        g->mark_dirty();
7 ✔
360
      }
7 ✔
361
    });
3,785 ✔
362
    return;
3,789 ✔
363
  }
364

365
  auto& qual = address.qualifiers.get();
425 ✔
366
  if(auto n = findNode(*plug.execState, address.address))
425 ✔
367
  {
368
    auto p = n->get_parameter();
424 ✔
369
    if(p)
424 ✔
370
    {
371
      append([ws = std::weak_ptr{s}, port, p, qual = qual, wg = std::weak_ptr{g}] {
838 ✔
372
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
414 ✔
373
        auto s = ws.lock();
414 ✔
374
        if(!s)
414 ✔
375
          return;
×
376
        auto g = wg.lock();
414 ✔
377
        if(!g)
414 ✔
378
          return;
×
379
        s->unregister_port(*port);
414 ✔
380
        port->address = p;
414 ✔
381
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
414 ✔
382
        {
383
          // Unconditionally, like the index: dropping the qualifier has to give
384
          // the process's declaration back.
385
          dat->address_unit = qual.unit;
231 ✔
386
          dat->index = qual.accessors;
231 ✔
387
        }
231 ✔
388
        s->register_port(*port);
414 ✔
389
        g->mark_dirty();
414 ✔
390
      });
414 ✔
391
    }
424 ✔
392
    else
393
    {
394
      append([ws = std::weak_ptr{s}, n, port, wg = std::weak_ptr{g}] {
×
395
        auto s = ws.lock();
×
396
        if(!s)
×
397
          return;
×
398
        auto g = wg.lock();
×
399
        if(!g)
×
400
          return;
×
401
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
402
        s->unregister_port(*port);
×
403
        port->address = n;
×
404
        s->register_port(*port);
×
405
        g->mark_dirty();
×
406
      });
×
407
    }
408
  }
424 ✔
409
  else if(auto ad = address.address.toString_unsafe().toStdString();
1 ✔
410
          ossia::traversal::is_pattern(ad))
2 ✔
411
  {
412
    // OPTIMIZEME
413
    auto path = ossia::traversal::make_path(ad);
×
414
    if(path)
×
415
    {
416
      append([ws = std::weak_ptr{s}, p = *path, port, wg = std::weak_ptr{g}]() mutable {
×
417
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
418
        auto s = ws.lock();
×
419
        if(!s)
×
420
          return;
×
421
        auto g = wg.lock();
×
422
        if(!g)
×
423
          return;
×
424
        s->unregister_port(*port);
×
425
        port->address = std::move(p);
×
426
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
×
427
        {
428
          dat->address_unit = {};
×
429
          dat->index.clear();
×
430
        }
×
431
        s->register_port(*port);
×
432
        g->mark_dirty();
×
433
      });
×
434
    }
×
435
    else
436
    {
437
      append([ws = std::weak_ptr{s}, n, port, wg = std::weak_ptr{g}] {
×
438
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
439
        auto s = ws.lock();
×
440
        if(!s)
×
441
          return;
×
442
        auto g = wg.lock();
×
443
        if(!g)
×
444
          return;
×
445
        s->unregister_port(*port);
×
446
        port->address = {};
×
447
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
×
448
        {
449
          dat->address_unit = {};
×
450
          dat->index.clear();
×
451
        }
×
452
        s->register_port(*port);
×
453
        g->mark_dirty();
×
454
      });
×
455
    }
456
  }
×
457
}
4,214 ✔
458

459
void SetupContext::set_destination(
69 ✔
460
    const State::AddressAccessor& address, const ossia::inlet_ptr& port)
461
{
462
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
69 ✔
463
  set_destination_impl(context, address, port, enqueue_in_context(*this));
69 ✔
464
}
69 ✔
465

466
void SetupContext::set_destination(
3 ✔
467
    const State::AddressAccessor& address, const ossia::outlet_ptr& port)
468
{
469
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
3 ✔
470
  set_destination_impl(context, address, port, enqueue_in_context(*this));
3 ✔
471
}
3 ✔
472

473
void SetupContext::register_inlet(
×
474
    Process::Inlet& inlet, const ossia::inlet_ptr& exec,
475
    const std::shared_ptr<ossia::graph_node>& node)
476
{
477
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
478
  register_inlet_impl(inlet, exec, node, enqueue_in_context(*this));
×
479
}
×
480
void SetupContext::register_outlet(
×
481
    Process::Outlet& outlet, const ossia::outlet_ptr& exec,
482
    const std::shared_ptr<ossia::graph_node>& node)
483
{
484
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
485
  register_outlet_impl(outlet, exec, node, enqueue_in_context(*this));
×
486
}
×
487

488
void SetupContext::register_node(
856 ✔
489
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
490
    const std::shared_ptr<ossia::graph_node>& node)
491
{
492
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
856 ✔
493
  register_node_impl(proc_inlets, proc_outlets, node, enqueue_in_context(*this));
856 ✔
494
}
856 ✔
495

496
void SetupContext::register_node(
180 ✔
497
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
498
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
499
{
500
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
180 ✔
501
  register_node_impl(proc_inlets, proc_outlets, node, enqueue_in_vector(vec));
180 ✔
502
}
180 ✔
503

504
void SetupContext::register_inlet(
701 ✔
505
    Process::Inlet& inlet, const ossia::inlet_ptr& exec,
506
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
507
{
508
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
701 ✔
509
  register_inlet_impl(inlet, exec, node, enqueue_in_vector(vec));
701 ✔
510
}
701 ✔
511
void SetupContext::register_outlet(
117 ✔
512
    Process::Outlet& outlet, const ossia::outlet_ptr& exec,
513
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
514
{
515
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
117 ✔
516
  register_outlet_impl(outlet, exec, node, enqueue_in_vector(vec));
117 ✔
517
}
117 ✔
518

519
void SetupContext::on_cableCreated(Process::Cable& c)
82 ✔
520
{
521
  if(context.retired)
82 ✔
NEW
522
    return;
×
523
  connectCable(c);
82 ✔
524
}
82 ✔
525

526
void SetupContext::on_cableRemoved(const Process::Cable& c)
87 ✔
527
{
528
  if(context.retired)
87 ✔
NEW
529
    return;
×
530
  removeCable(c);
87 ✔
531
}
87 ✔
532

533
void SetupContext::connectCable(Process::Cable& c)
82 ✔
534
{
535
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
82 ✔
536
  connect_cable_impl(c, enqueue_in_context(*this));
82 ✔
537
}
82 ✔
538

539
void SetupContext::connectCable(Process::Cable& c, Transaction& vec)
36 ✔
540
{
541
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
36 ✔
542
  connect_cable_impl(c, enqueue_in_vector(vec));
36 ✔
543
}
36 ✔
544

545
void SetupContext::removeCable(const Process::Cable& c)
87 ✔
546
{
547
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
87 ✔
548
  disconnect_cable_impl(c, enqueue_in_context(*this));
87 ✔
549
}
87 ✔
550

551
void SetupContext::removeCable(const Process::Cable& c, Transaction& vec)
16 ✔
552
{
553
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
16 ✔
554
  disconnect_cable_impl(c, enqueue_in_vector(vec));
16 ✔
555
}
16 ✔
556

557
template <typename Impl>
558
void SetupContext::register_outlet_impl(
857 ✔
559
    Process::Outlet& proc_port, const ossia::outlet_ptr& ossia_port,
560
    const std::shared_ptr<ossia::graph_node>& node, Impl&& impl)
561
{
562
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
857 ✔
563
  SCORE_ASSERT(node);
857 ✔
564
  SCORE_ASSERT(ossia_port);
857 ✔
565
  bind_address(
857 ✔
566
      proc_port, ossia_port, runtime_connections[node].outlets[proc_port.id()], impl);
857 ✔
567

568
  outlets.insert({&proc_port, std::make_pair(node, ossia_port)});
857 ✔
569

570
  proc_port.mapExecution(
1,714 ✔
571
      *ossia_port, [&](Process::Inlet& model_inl, ossia::inlet& ossia_inl) {
1,591 ✔
572
        register_inlet_impl(model_inl, &ossia_inl, node, impl);
734 ✔
573
      });
734 ✔
574

575
  // Unneeded : the execution_state only needs inlets to be registered,
576
  // in order to set up data value queues from the network thread
577

578
  // std::weak_ptr<ossia::execution_state> ws = context.execState;
579
  // impl([ws, ossia_port] {
580
  //   if (auto state = ws.lock())
581
  //     state->register_outlet(*ossia_port);
582
  // });
583
}
857 ✔
584

585
void SetupContext::unregister_inlet(
×
586
    const Process::Inlet& proc_port, const std::shared_ptr<ossia::graph_node>& node)
587
{
588
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
589
  if(node)
×
590
  {
591
    auto& runtime_connection = runtime_connections[node].inlets;
×
592
    auto it = runtime_connection.find(proc_port.id());
×
593
    if(it != runtime_connection.end())
×
594
    {
595
      for(auto& con : it->second)
×
596
        QObject::disconnect(con);
×
597
      runtime_connection.erase(it);
×
598
    }
×
599

600
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
601
    if(ossia_port_it != inlets.end())
×
602
    {
603
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
604
      context.executionQueue.enqueue([ws, ossia_port = ossia_port_it->second.second] {
×
605
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
606
        if(auto state = ws.lock())
×
607
          state->unregister_port(*ossia_port);
×
608
      });
×
609

610
      inlets.erase(ossia_port_it);
×
611
    }
×
612
  }
×
613
  else
614
  {
615
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
616
  }
617
}
×
618

619
void SetupContext::unregister_outlet(
×
620
    const Process::Outlet& proc_port, const std::shared_ptr<ossia::graph_node>& node)
621
{
622
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
623
  if(node)
×
624
  {
625
    auto& runtime_connection = runtime_connections[node].outlets;
×
626
    auto it = runtime_connection.find(proc_port.id());
×
627
    if(it != runtime_connection.end())
×
628
    {
629
      for(auto& con : it->second)
×
630
        QObject::disconnect(con);
×
631
      runtime_connection.erase(it);
×
632
    }
×
633

634
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
635
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
636
      unregister_inlet(model_inl, node);
×
637
    });
×
638
  }
×
639

640
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
641
}
×
642

643
void SetupContext::unregister_inlet(
×
644
    const Process::Inlet& proc_port, const std::shared_ptr<ossia::graph_node>& node,
645
    Transaction& commands)
646
{
647
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
648
  if(node)
×
649
  {
650
    auto& runtime_connection = runtime_connections[node].inlets;
×
651
    auto it = runtime_connection.find(proc_port.id());
×
652
    if(it != runtime_connection.end())
×
653
    {
654
      for(auto& con : it->second)
×
655
        QObject::disconnect(con);
×
656
      runtime_connection.erase(it);
×
657
    }
×
658

659
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
660
    if(ossia_port_it != inlets.end())
×
661
    {
662
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
663
      commands.push_back([ws, ossia_port = ossia_port_it->second.second] {
×
664
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
665
        if(auto state = ws.lock())
×
666
          state->unregister_port(*ossia_port);
×
667
      });
×
668

669
      inlets.erase(ossia_port_it);
×
670
    }
×
671
  }
×
672
  else
673
  {
674
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
675
  }
676
}
×
677

678
void SetupContext::unregister_outlet(
×
679
    const Process::Outlet& proc_port, const std::shared_ptr<ossia::graph_node>& node,
680
    Transaction& commands)
681
{
682
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
683
  if(node)
×
684
  {
685
    auto& runtime_connection = runtime_connections[node].outlets;
×
686
    auto it = runtime_connection.find(proc_port.id());
×
687
    if(it != runtime_connection.end())
×
688
    {
689
      for(auto& con : it->second)
×
690
        QObject::disconnect(con);
×
691
      runtime_connection.erase(it);
×
692
    }
×
693

694
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
695
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
696
      unregister_inlet(model_inl, node);
×
697
    });
×
698
  }
×
699

700
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
701
}
×
702

703
void SetupContext::replace_node(
×
704
    const std::shared_ptr<ossia::time_process>& process,
705
    const std::shared_ptr<ossia::graph_node>& node, Transaction& commands)
706
{
707
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
708
  commands.push_back([p = process, n = node]() mutable {
×
709
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
710
    using namespace std;
711
    swap(p->node, n);
×
712
  });
×
713
}
×
714

715
void SetupContext::unregister_node(
743 ✔
716
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
717
    const std::shared_ptr<ossia::graph_node>& node)
718
{
719
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
743 ✔
720
  if(node)
743 ✔
721
  {
722
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
743 ✔
723
    std::weak_ptr<ossia::execution_state> ws = context.execState;
743 ✔
724
    context.executionQueue.enqueue([wg, ws, node = node] mutable {
1,186 ✔
725
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
443 ✔
726
      if(auto s = ws.lock())
886 ✔
727
      {
728
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
1,647 ✔
729
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
852 ✔
730
      }
443 ✔
731

732
      if(auto g = wg.lock())
886 ✔
733
        g->remove_node(node);
443 ✔
734

735
      node->clear();
443 ✔
736
    });
443 ✔
737

738
    runtime_connections[node].clear();
743 ✔
739
    runtime_connections.erase(node);
743 ✔
740

741
    proc_map.erase(node.get());
743 ✔
742
  }
743 ✔
743

744
  for(auto ptr : proc_inlets)
2,518 ✔
745
    inlets.erase(ptr);
1,775 ✔
746
  for(auto ptr : proc_outlets)
1,394 ✔
747
    outlets.erase(ptr);
651 ✔
748
}
743 ✔
749

750
void SetupContext::unregister_node(
×
751
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
752
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
753
{
754
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
755
  if(node)
×
756
  {
757
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
×
758
    std::weak_ptr<ossia::execution_state> ws = context.execState;
×
759
    vec.push_back([wg, ws, node] {
×
760
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
761
      if(auto s = ws.lock())
×
762
      {
763
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
×
764
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
×
765
      }
×
766

767
      if(auto g = wg.lock())
×
768
        g->remove_node(node);
×
769
      node->clear();
×
770
    });
×
771

772
    runtime_connections[node].clear();
×
773
    runtime_connections.erase(node);
×
774

775
    proc_map.erase(node.get());
×
776
  }
×
777

778
  for(auto ptr : proc_inlets)
×
779
    inlets.erase(ptr);
×
780
  for(auto ptr : proc_outlets)
×
781
    outlets.erase(ptr);
×
782
}
×
783

784
void SetupContext::unregister_node_soft(
78 ✔
785
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
786
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
787
{
788
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
78 ✔
789
  if(node)
78 ✔
790
  {
791
    std::weak_ptr<ossia::execution_state> ws = context.execState;
78 ✔
792
    vec.push_back([ws, node] {
155 ✔
793
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
77 ✔
794
      if(auto s = ws.lock())
154 ✔
795
      {
796
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
790 ✔
797
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
184 ✔
798
      }
77 ✔
799
    });
77 ✔
800
    runtime_connections[node].clear();
78 ✔
801
    runtime_connections.erase(node);
78 ✔
802

803
    proc_map.erase(node.get());
78 ✔
804
  }
78 ✔
805

806
  for(auto ptr : proc_inlets)
758 ✔
807
    inlets.erase(ptr);
680 ✔
808
  for(auto ptr : proc_outlets)
189 ✔
809
    outlets.erase(ptr);
111 ✔
810
}
78 ✔
811

812
SetupContext::SetupContext(Context& other) noexcept
4,908 ✔
813
    : context{other}
4,908 ✔
814
{
9,816 ✔
815
}
816

817
void SetupContext::follow_published()
4,142 ✔
818
{
819
  if(m_followsPublished)
4,142 ✔
820
    return;
3,991 ✔
821
  m_followsPublished = true;
151 ✔
822
  // Rebinds ports published after setup, e.g. restored by undo during playback
823
  if(auto tree = context.doc.findPlugin<LocalTree::ScriptableTreeBase>())
151 ✔
824
    connect(tree, &LocalTree::ScriptableTreeBase::published, this, [this](QObject* object) {
212 ✔
825
      QTimer::singleShot(0, this, [this, object = QPointer<QObject>{object}] {
122 ✔
826
        if(object)
61 ✔
827
          rebind(object.data());
61 ✔
828
      });
61 ✔
829
    });
61 ✔
830
}
4,142 ✔
831

832
void SetupContext::rebind(QObject* port)
88 ✔
833
{
834
  if(context.retired)
88 ✔
NEW
835
    return;
×
836
  if(auto in = qobject_cast<Process::Inlet*>(port))
88 ✔
837
  {
838
    if(auto it = inlets.find(in); it != inlets.end())
69 ✔
839
      set_destination(effectiveAddress(*in), it->second.second);
69 ✔
840
  }
69 ✔
841
  else if(auto out = qobject_cast<Process::Outlet*>(port))
19 ✔
842
  {
843
    if(auto it = outlets.find(out); it != outlets.end())
3 ✔
844
      set_destination(effectiveAddress(*out), it->second.second);
3 ✔
845
  }
3 ✔
846
}
88 ✔
847

848
SetupContext::~SetupContext() { }
4,908 ✔
849

850
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc