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

ossia / score / 38109437529

11 Oct 2026 03:45AM UTC coverage: 48.078%. First build
38109437529

Pull #2331

github

web-flow
Merge 56e14c66c into 4b2f7f45e
Pull Request #2331: gfx: close a camera with its node, give its fallback open a budget, and pick Syphon's decoder from the RHI

12 of 29 new or added lines in 4 files covered. (41.38%)

156509 of 325534 relevant lines covered (48.08%)

146417.52 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,095 ✔
55
{
56
  return ContextEnqueuer{self};
1,095 ✔
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)
102 ✔
93
{
94
  if(!context.created) {
102 ✔
95
    return;
81 ✔
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
}
102 ✔
110

111
template <typename Impl>
112
void SetupContext::connect_cable_impl(Process::Cable& cable, Impl&& impl)
117 ✔
113
{
114
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
117 ✔
115
  if(!context.created) {
117 ✔
116
    return;
75 ✔
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
}
117 ✔
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
  // findNode() below walks plug.execState's device list and calls get_name() on
334
  // each entry; get_name() is non-virtual over the pure virtual
335
  // get_root_node(), so one destroyed device aborts the process. A retired
336
  // context's state is full of them.
337
  if(plug.retired)
4,214 ✔
NEW
338
    return;
×
339
  auto& s = plug.execState;
4,214 ✔
340
  auto& g = plug.execGraph;
4,214 ✔
341
  if(!g)
4,214 ✔
342
    return;
×
343

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

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

463
void SetupContext::set_destination(
69 ✔
464
    const State::AddressAccessor& address, const ossia::inlet_ptr& port)
465
{
466
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
69 ✔
467
  set_destination_impl(context, address, port, enqueue_in_context(*this));
69 ✔
468
}
69 ✔
469

470
void SetupContext::set_destination(
3 ✔
471
    const State::AddressAccessor& address, const ossia::outlet_ptr& port)
472
{
473
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
3 ✔
474
  set_destination_impl(context, address, port, enqueue_in_context(*this));
3 ✔
475
}
3 ✔
476

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

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

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

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

523
void SetupContext::on_cableCreated(Process::Cable& c)
81 ✔
524
{
525
  // Nothing disconnects this from model.cables when the context is replaced:
526
  // SetupContext is a Nano::Observer, which only unsubscribes in its
527
  // destructor, and a retired ContextData is not reliably destroyed.
528
  if(context.retired)
81 ✔
NEW
529
    return;
×
530
  connectCable(c);
81 ✔
531
}
81 ✔
532

533
void SetupContext::on_cableRemoved(const Process::Cable& c)
86 ✔
534
{
535
  if(context.retired)
86 ✔
NEW
536
    return;
×
537
  removeCable(c);
86 ✔
538
}
86 ✔
539

540
void SetupContext::connectCable(Process::Cable& c)
81 ✔
541
{
542
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
81 ✔
543
  connect_cable_impl(c, enqueue_in_context(*this));
81 ✔
544
}
81 ✔
545

546
void SetupContext::connectCable(Process::Cable& c, Transaction& vec)
36 ✔
547
{
548
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
36 ✔
549
  connect_cable_impl(c, enqueue_in_vector(vec));
36 ✔
550
}
36 ✔
551

552
void SetupContext::removeCable(const Process::Cable& c)
86 ✔
553
{
554
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
86 ✔
555
  disconnect_cable_impl(c, enqueue_in_context(*this));
86 ✔
556
}
86 ✔
557

558
void SetupContext::removeCable(const Process::Cable& c, Transaction& vec)
16 ✔
559
{
560
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
16 ✔
561
  disconnect_cable_impl(c, enqueue_in_vector(vec));
16 ✔
562
}
16 ✔
563

564
template <typename Impl>
565
void SetupContext::register_outlet_impl(
857 ✔
566
    Process::Outlet& proc_port, const ossia::outlet_ptr& ossia_port,
567
    const std::shared_ptr<ossia::graph_node>& node, Impl&& impl)
568
{
569
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
857 ✔
570
  SCORE_ASSERT(node);
857 ✔
571
  SCORE_ASSERT(ossia_port);
857 ✔
572
  bind_address(
857 ✔
573
      proc_port, ossia_port, runtime_connections[node].outlets[proc_port.id()], impl);
857 ✔
574

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

577
  proc_port.mapExecution(
1,714 ✔
578
      *ossia_port, [&](Process::Inlet& model_inl, ossia::inlet& ossia_inl) {
1,591 ✔
579
        register_inlet_impl(model_inl, &ossia_inl, node, impl);
734 ✔
580
      });
734 ✔
581

582
  // Unneeded : the execution_state only needs inlets to be registered,
583
  // in order to set up data value queues from the network thread
584

585
  // std::weak_ptr<ossia::execution_state> ws = context.execState;
586
  // impl([ws, ossia_port] {
587
  //   if (auto state = ws.lock())
588
  //     state->register_outlet(*ossia_port);
589
  // });
590
}
857 ✔
591

592
void SetupContext::unregister_inlet(
×
593
    const Process::Inlet& proc_port, const std::shared_ptr<ossia::graph_node>& node)
594
{
595
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
596
  if(node)
×
597
  {
598
    auto& runtime_connection = runtime_connections[node].inlets;
×
599
    auto it = runtime_connection.find(proc_port.id());
×
600
    if(it != runtime_connection.end())
×
601
    {
602
      for(auto& con : it->second)
×
603
        QObject::disconnect(con);
×
604
      runtime_connection.erase(it);
×
605
    }
×
606

607
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
608
    if(ossia_port_it != inlets.end())
×
609
    {
610
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
611
      context.executionQueue.enqueue([ws, ossia_port = ossia_port_it->second.second] {
×
612
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
613
        if(auto state = ws.lock())
×
614
          state->unregister_port(*ossia_port);
×
615
      });
×
616

617
      inlets.erase(ossia_port_it);
×
618
    }
×
619
  }
×
620
  else
621
  {
622
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
623
  }
624
}
×
625

626
void SetupContext::unregister_outlet(
×
627
    const Process::Outlet& proc_port, const std::shared_ptr<ossia::graph_node>& node)
628
{
629
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
630
  if(node)
×
631
  {
632
    auto& runtime_connection = runtime_connections[node].outlets;
×
633
    auto it = runtime_connection.find(proc_port.id());
×
634
    if(it != runtime_connection.end())
×
635
    {
636
      for(auto& con : it->second)
×
637
        QObject::disconnect(con);
×
638
      runtime_connection.erase(it);
×
639
    }
×
640

641
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
642
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
643
      unregister_inlet(model_inl, node);
×
644
    });
×
645
  }
×
646

647
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
648
}
×
649

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

666
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
667
    if(ossia_port_it != inlets.end())
×
668
    {
669
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
670
      commands.push_back([ws, ossia_port = ossia_port_it->second.second] {
×
671
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
672
        if(auto state = ws.lock())
×
673
          state->unregister_port(*ossia_port);
×
674
      });
×
675

676
      inlets.erase(ossia_port_it);
×
677
    }
×
678
  }
×
679
  else
680
  {
681
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
682
  }
683
}
×
684

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

701
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
702
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
703
      unregister_inlet(model_inl, node);
×
704
    });
×
705
  }
×
706

707
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
708
}
×
709

710
void SetupContext::replace_node(
×
711
    const std::shared_ptr<ossia::time_process>& process,
712
    const std::shared_ptr<ossia::graph_node>& node, Transaction& commands)
713
{
714
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
715
  commands.push_back([p = process, n = node]() mutable {
×
716
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
717
    using namespace std;
718
    swap(p->node, n);
×
719
  });
×
720
}
×
721

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

739
      if(auto g = wg.lock())
886 ✔
740
        g->remove_node(node);
443 ✔
741

742
      node->clear();
443 ✔
743
    });
443 ✔
744

745
    runtime_connections[node].clear();
743 ✔
746
    runtime_connections.erase(node);
743 ✔
747

748
    proc_map.erase(node.get());
743 ✔
749
  }
743 ✔
750

751
  for(auto ptr : proc_inlets)
2,518 ✔
752
    inlets.erase(ptr);
1,775 ✔
753
  for(auto ptr : proc_outlets)
1,394 ✔
754
    outlets.erase(ptr);
651 ✔
755
}
743 ✔
756

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

774
      if(auto g = wg.lock())
×
775
        g->remove_node(node);
×
776
      node->clear();
×
777
    });
×
778

779
    runtime_connections[node].clear();
×
780
    runtime_connections.erase(node);
×
781

782
    proc_map.erase(node.get());
×
783
  }
×
784

785
  for(auto ptr : proc_inlets)
×
786
    inlets.erase(ptr);
×
787
  for(auto ptr : proc_outlets)
×
788
    outlets.erase(ptr);
×
789
}
×
790

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

810
    proc_map.erase(node.get());
78 ✔
811
  }
78 ✔
812

813
  for(auto ptr : proc_inlets)
758 ✔
814
    inlets.erase(ptr);
680 ✔
815
  for(auto ptr : proc_outlets)
189 ✔
816
    outlets.erase(ptr);
111 ✔
817
}
78 ✔
818

819
SetupContext::SetupContext(Context& other) noexcept
4,795 ✔
820
    : context{other}
4,795 ✔
821
{
9,590 ✔
822
}
823

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

839
void SetupContext::rebind(QObject* port)
88 ✔
840
{
841
  // Each SetupContext connects this to every port it binds and disconnects
842
  // only its own earlier connections, so a port signal reaches every live
843
  // SetupContext, retired ones included.
844
  if(context.retired)
88 ✔
NEW
845
    return;
×
846
  if(auto in = qobject_cast<Process::Inlet*>(port))
88 ✔
847
  {
848
    if(auto it = inlets.find(in); it != inlets.end())
69 ✔
849
      set_destination(effectiveAddress(*in), it->second.second);
69 ✔
850
  }
69 ✔
851
  else if(auto out = qobject_cast<Process::Outlet*>(port))
19 ✔
852
  {
853
    if(auto it = outlets.find(out); it != outlets.end())
3 ✔
854
      set_destination(effectiveAddress(*out), it->second.second);
3 ✔
855
  }
3 ✔
856
}
88 ✔
857

858
SetupContext::~SetupContext() { }
4,795 ✔
859

860
}
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