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

ossia / score / 39156395020

18 Oct 2026 06:55PM UTC coverage: 49.156% (+0.3%) from 48.887%
39156395020

push

github

jcelerier
avendish: mono and poly audio nodes process the tick's own samples

celtera/avendish#223: a tick starting mid-buffer read and wrote at
offset 0 of the channels in the mono and poly nodes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JeL19ofVgPe9u9q7J1V9ex

163551 of 332721 relevant lines covered (49.16%)

146191.73 hits per line

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

62.14
/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
    const std::shared_ptr<ossia::graph_node>& node, RegisteredPorts::connections& cons,
214
    Impl&& impl)
215
{
216
  for(auto& con : cons)
4,142 ✔
217
    QObject::disconnect(con);
×
218
  cons.clear();
4,142 ✔
219

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

329
static bool hasPort(const ossia::graph_node& node, const ossia::inlet* port) noexcept
3,340 ✔
330
{
331
  return ossia::any_of_inlet(node, [port](const ossia::inlet& p) { return &p == port; });
22,732 ✔
332
}
333
static bool hasPort(const ossia::graph_node& node, const ossia::outlet* port) noexcept
859 ✔
334
{
335
  return ossia::any_of_outlet(node, [port](const ossia::outlet& p) { return &p == port; });
2,053 ✔
336
}
337

338
template <typename T, typename Impl>
339
void set_destination_impl(
4,214 ✔
340
    const Context& plug, const State::AddressAccessor& address, const T& port,
341
    const std::shared_ptr<ossia::graph_node>& owner, Impl&& enqueue)
342
{
343
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
4,214 ✔
344
  if(plug.retired)
4,214 ✔
345
    return;
×
346

347
  // The commands run later on the execution thread and only hold the raw
348
  // ossia port: by then an edit (undo/redo, a program change) may have
349
  // removed the node or the port. Skip them when the port's node is gone or no
350
  // longer has it. The port is captured once, here, and handed to the command:
351
  // the whole closure has to fit an ExecutionCommand.
352
  auto append = [&enqueue, &owner, port]<typename F>(F&& f) {
8,427 ✔
353
    if(!owner)
4,213 ✔
354
    {
355
      enqueue([f = std::forward<F>(f), port]() mutable { f(port); });
×
356
      return;
×
357
    }
358
    enqueue([f = std::forward<F>(f), wn = std::weak_ptr{owner}, port]() mutable {
8,412 ✔
359
      auto n = wn.lock();
4,199 ✔
360
      if(!n || !hasPort(*n, port))
4,199 ✔
361
        return;
×
362
      f(port);
4,199 ✔
363
    });
4,199 ✔
364
  };
4,213 ✔
365
  auto& s = plug.execState;
4,214 ✔
366
  auto& g = plug.execGraph;
4,214 ✔
367
  if(!g)
4,214 ✔
368
    return;
×
369

370
  if(address.address.device.isEmpty())
4,214 ✔
371
  {
372
    append([ws = std::weak_ptr{s}, wg = std::weak_ptr{g}](const T& port) {
7,574 ✔
373
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
3,785 ✔
374
      auto s = ws.lock();
3,785 ✔
375
      if(!s)
3,785 ✔
376
        return;
×
377
      auto g = wg.lock();
3,785 ✔
378
      if(!g)
3,785 ✔
379
        return;
×
380
      if(port->address)
3,785 ✔
381
      {
382
        s->unregister_port(*port);
7 ✔
383
        port->address = {};
7 ✔
384
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
7 ✔
385
        {
386
          dat->address_unit = {};
7 ✔
387
          dat->index = {};
7 ✔
388
        }
7 ✔
389
        g->mark_dirty();
7 ✔
390
      }
7 ✔
391
    });
3,785 ✔
392
    return;
3,789 ✔
393
  }
394

395
  auto& qual = address.qualifiers.get();
425 ✔
396
  if(auto n = findNode(*plug.execState, address.address))
425 ✔
397
  {
398
    auto p = n->get_parameter();
424 ✔
399
    if(p)
424 ✔
400
    {
401
      append([ws = std::weak_ptr{s}, p, qual = qual, wg = std::weak_ptr{g}](const T& port) {
838 ✔
402
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
414 ✔
403
        auto s = ws.lock();
414 ✔
404
        if(!s)
414 ✔
405
          return;
×
406
        auto g = wg.lock();
414 ✔
407
        if(!g)
414 ✔
408
          return;
×
409
        s->unregister_port(*port);
414 ✔
410
        port->address = p;
414 ✔
411
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
414 ✔
412
        {
413
          // Unconditionally, like the index: dropping the qualifier has to give
414
          // the process's declaration back.
415
          dat->address_unit = qual.unit;
231 ✔
416
          dat->index = qual.accessors;
231 ✔
417
        }
231 ✔
418
        s->register_port(*port);
414 ✔
419
        g->mark_dirty();
414 ✔
420
      });
414 ✔
421
    }
424 ✔
422
    else
423
    {
424
      append([ws = std::weak_ptr{s}, n, wg = std::weak_ptr{g}](const T& port) {
×
425
        auto s = ws.lock();
×
426
        if(!s)
×
427
          return;
×
428
        auto g = wg.lock();
×
429
        if(!g)
×
430
          return;
×
431
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
432
        s->unregister_port(*port);
×
433
        port->address = n;
×
434
        s->register_port(*port);
×
435
        g->mark_dirty();
×
436
      });
×
437
    }
438
  }
424 ✔
439
  else if(auto ad = address.address.toString_unsafe().toStdString();
1 ✔
440
          ossia::traversal::is_pattern(ad))
2 ✔
441
  {
442
    // OPTIMIZEME
443
    auto path = ossia::traversal::make_path(ad);
×
444
    if(path)
×
445
    {
446
      append([ws = std::weak_ptr{s}, p = *path, wg = std::weak_ptr{g}](const T& port) mutable {
×
447
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
448
        auto s = ws.lock();
×
449
        if(!s)
×
450
          return;
×
451
        auto g = wg.lock();
×
452
        if(!g)
×
453
          return;
×
454
        s->unregister_port(*port);
×
455
        port->address = std::move(p);
×
456
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
×
457
        {
458
          dat->address_unit = {};
×
459
          dat->index.clear();
×
460
        }
×
461
        s->register_port(*port);
×
462
        g->mark_dirty();
×
463
      });
×
464
    }
×
465
    else
466
    {
467
      append([ws = std::weak_ptr{s}, n, wg = std::weak_ptr{g}](const T& port) {
×
468
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
469
        auto s = ws.lock();
×
470
        if(!s)
×
471
          return;
×
472
        auto g = wg.lock();
×
473
        if(!g)
×
474
          return;
×
475
        s->unregister_port(*port);
×
476
        port->address = {};
×
477
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
×
478
        {
479
          dat->address_unit = {};
×
480
          dat->index.clear();
×
481
        }
×
482
        s->register_port(*port);
×
483
        g->mark_dirty();
×
484
      });
×
485
    }
486
  }
×
487
}
4,214 ✔
488

489
void SetupContext::set_destination(
×
490
    const State::AddressAccessor& address, const ossia::inlet_ptr& port)
491
{
492
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
493
  set_destination_impl(context, address, port, {}, enqueue_in_context(*this));
×
494
}
×
495

496
void SetupContext::set_destination(
×
497
    const State::AddressAccessor& address, const ossia::outlet_ptr& port)
498
{
499
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
500
  set_destination_impl(context, address, port, {}, enqueue_in_context(*this));
×
501
}
×
502

503
void SetupContext::register_inlet(
×
504
    Process::Inlet& inlet, const ossia::inlet_ptr& exec,
505
    const std::shared_ptr<ossia::graph_node>& node)
506
{
507
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
508
  register_inlet_impl(inlet, exec, node, enqueue_in_context(*this));
×
509
}
×
510
void SetupContext::register_outlet(
×
511
    Process::Outlet& outlet, const ossia::outlet_ptr& exec,
512
    const std::shared_ptr<ossia::graph_node>& node)
513
{
514
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
515
  register_outlet_impl(outlet, exec, node, enqueue_in_context(*this));
×
516
}
×
517

518
void SetupContext::register_node(
856 ✔
519
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
520
    const std::shared_ptr<ossia::graph_node>& node)
521
{
522
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
856 ✔
523
  register_node_impl(proc_inlets, proc_outlets, node, enqueue_in_context(*this));
856 ✔
524
}
856 ✔
525

526
void SetupContext::register_node(
180 ✔
527
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
528
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
529
{
530
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
180 ✔
531
  register_node_impl(proc_inlets, proc_outlets, node, enqueue_in_vector(vec));
180 ✔
532
}
180 ✔
533

534
void SetupContext::register_inlet(
701 ✔
535
    Process::Inlet& inlet, const ossia::inlet_ptr& exec,
536
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
537
{
538
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
701 ✔
539
  register_inlet_impl(inlet, exec, node, enqueue_in_vector(vec));
701 ✔
540
}
701 ✔
541
void SetupContext::register_outlet(
117 ✔
542
    Process::Outlet& outlet, const ossia::outlet_ptr& exec,
543
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
544
{
545
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
117 ✔
546
  register_outlet_impl(outlet, exec, node, enqueue_in_vector(vec));
117 ✔
547
}
117 ✔
548

549
void SetupContext::on_cableCreated(Process::Cable& c)
82 ✔
550
{
551
  if(context.retired)
82 ✔
552
    return;
×
553
  connectCable(c);
82 ✔
554
}
82 ✔
555

556
void SetupContext::on_cableRemoved(const Process::Cable& c)
87 ✔
557
{
558
  if(context.retired)
87 ✔
559
    return;
×
560
  removeCable(c);
87 ✔
561
}
87 ✔
562

563
void SetupContext::connectCable(Process::Cable& c)
82 ✔
564
{
565
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
82 ✔
566
  connect_cable_impl(c, enqueue_in_context(*this));
82 ✔
567
}
82 ✔
568

569
void SetupContext::connectCable(Process::Cable& c, Transaction& vec)
36 ✔
570
{
571
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
36 ✔
572
  connect_cable_impl(c, enqueue_in_vector(vec));
36 ✔
573
}
36 ✔
574

575
void SetupContext::removeCable(const Process::Cable& c)
87 ✔
576
{
577
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
87 ✔
578
  disconnect_cable_impl(c, enqueue_in_context(*this));
87 ✔
579
}
87 ✔
580

581
void SetupContext::removeCable(const Process::Cable& c, Transaction& vec)
16 ✔
582
{
583
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
16 ✔
584
  disconnect_cable_impl(c, enqueue_in_vector(vec));
16 ✔
585
}
16 ✔
586

587
template <typename Impl>
588
void SetupContext::register_outlet_impl(
857 ✔
589
    Process::Outlet& proc_port, const ossia::outlet_ptr& ossia_port,
590
    const std::shared_ptr<ossia::graph_node>& node, Impl&& impl)
591
{
592
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
857 ✔
593
  SCORE_ASSERT(node);
857 ✔
594
  SCORE_ASSERT(ossia_port);
857 ✔
595
  bind_address(
857 ✔
596
      proc_port, ossia_port, node, runtime_connections[node].outlets[proc_port.id()],
857 ✔
597
      impl);
857 ✔
598

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

601
  proc_port.mapExecution(
1,714 ✔
602
      *ossia_port, [&](Process::Inlet& model_inl, ossia::inlet& ossia_inl) {
1,591 ✔
603
        register_inlet_impl(model_inl, &ossia_inl, node, impl);
734 ✔
604
      });
734 ✔
605

606
  // Unneeded : the execution_state only needs inlets to be registered,
607
  // in order to set up data value queues from the network thread
608

609
  // std::weak_ptr<ossia::execution_state> ws = context.execState;
610
  // impl([ws, ossia_port] {
611
  //   if (auto state = ws.lock())
612
  //     state->register_outlet(*ossia_port);
613
  // });
614
}
857 ✔
615

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

631
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
632
    if(ossia_port_it != inlets.end())
×
633
    {
634
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
635
      context.executionQueue.enqueue([ws, ossia_port = ossia_port_it->second.second] {
×
636
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
637
        if(auto state = ws.lock())
×
638
          state->unregister_port(*ossia_port);
×
639
      });
×
640

641
      inlets.erase(ossia_port_it);
×
642
    }
×
643
  }
×
644
  else
645
  {
646
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
647
  }
648
}
×
649

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

665
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
666
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
667
      unregister_inlet(model_inl, node);
×
668
    });
×
669
  }
×
670

671
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
672
}
×
673

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

690
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
691
    if(ossia_port_it != inlets.end())
×
692
    {
693
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
694
      commands.push_back([ws, ossia_port = ossia_port_it->second.second] {
×
695
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
696
        if(auto state = ws.lock())
×
697
          state->unregister_port(*ossia_port);
×
698
      });
×
699

700
      inlets.erase(ossia_port_it);
×
701
    }
×
702
  }
×
703
  else
704
  {
705
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
706
  }
707
}
×
708

709
void SetupContext::unregister_outlet(
×
710
    const Process::Outlet& proc_port, const std::shared_ptr<ossia::graph_node>& node,
711
    Transaction& commands)
712
{
713
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
714
  if(node)
×
715
  {
716
    auto& runtime_connection = runtime_connections[node].outlets;
×
717
    auto it = runtime_connection.find(proc_port.id());
×
718
    if(it != runtime_connection.end())
×
719
    {
720
      for(auto& con : it->second)
×
721
        QObject::disconnect(con);
×
722
      runtime_connection.erase(it);
×
723
    }
×
724

725
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
726
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
727
      unregister_inlet(model_inl, node);
×
728
    });
×
729
  }
×
730

731
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
732
}
×
733

734
void SetupContext::replace_node(
×
735
    const std::shared_ptr<ossia::time_process>& process,
736
    const std::shared_ptr<ossia::graph_node>& node, Transaction& commands)
737
{
738
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
739
  commands.push_back([p = process, n = node]() mutable {
×
740
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
741
    using namespace std;
742
    swap(p->node, n);
×
743
  });
×
744
}
×
745

746
void SetupContext::unregister_node(
743 ✔
747
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
748
    const std::shared_ptr<ossia::graph_node>& node)
749
{
750
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
743 ✔
751
  if(node)
743 ✔
752
  {
753
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
743 ✔
754
    std::weak_ptr<ossia::execution_state> ws = context.execState;
743 ✔
755
    context.executionQueue.enqueue([wg, ws, node = node] mutable {
1,186 ✔
756
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
443 ✔
757
      if(auto s = ws.lock())
886 ✔
758
      {
759
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
1,647 ✔
760
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
852 ✔
761
      }
443 ✔
762

763
      if(auto g = wg.lock())
886 ✔
764
        g->remove_node(node);
443 ✔
765

766
      node->clear();
443 ✔
767
    });
443 ✔
768

769
    runtime_connections[node].clear();
743 ✔
770
    runtime_connections.erase(node);
743 ✔
771

772
    proc_map.erase(node.get());
743 ✔
773
  }
743 ✔
774

775
  for(auto ptr : proc_inlets)
2,518 ✔
776
    inlets.erase(ptr);
1,775 ✔
777
  for(auto ptr : proc_outlets)
1,394 ✔
778
    outlets.erase(ptr);
651 ✔
779
}
743 ✔
780

781
void SetupContext::unregister_node(
×
782
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
783
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
784
{
785
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
786
  if(node)
×
787
  {
788
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
×
789
    std::weak_ptr<ossia::execution_state> ws = context.execState;
×
790
    vec.push_back([wg, ws, node] {
×
791
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
792
      if(auto s = ws.lock())
×
793
      {
794
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
×
795
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
×
796
      }
×
797

798
      if(auto g = wg.lock())
×
799
        g->remove_node(node);
×
800
      node->clear();
×
801
    });
×
802

803
    runtime_connections[node].clear();
×
804
    runtime_connections.erase(node);
×
805

806
    proc_map.erase(node.get());
×
807
  }
×
808

809
  for(auto ptr : proc_inlets)
×
810
    inlets.erase(ptr);
×
811
  for(auto ptr : proc_outlets)
×
812
    outlets.erase(ptr);
×
813
}
×
814

815
void SetupContext::unregister_node_soft(
78 ✔
816
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
817
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
818
{
819
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
78 ✔
820
  if(node)
78 ✔
821
  {
822
    std::weak_ptr<ossia::execution_state> ws = context.execState;
78 ✔
823
    vec.push_back([ws, node] {
155 ✔
824
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
77 ✔
825
      if(auto s = ws.lock())
154 ✔
826
      {
827
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
790 ✔
828
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
184 ✔
829
      }
77 ✔
830
    });
77 ✔
831
    runtime_connections[node].clear();
78 ✔
832
    runtime_connections.erase(node);
78 ✔
833

834
    proc_map.erase(node.get());
78 ✔
835
  }
78 ✔
836

837
  for(auto ptr : proc_inlets)
758 ✔
838
    inlets.erase(ptr);
680 ✔
839
  for(auto ptr : proc_outlets)
189 ✔
840
    outlets.erase(ptr);
111 ✔
841
}
78 ✔
842

843
SetupContext::SetupContext(Context& other) noexcept
4,916 ✔
844
    : context{other}
4,916 ✔
845
{
9,832 ✔
846
}
847

848
void SetupContext::follow_published()
4,142 ✔
849
{
850
  if(m_followsPublished)
4,142 ✔
851
    return;
3,991 ✔
852
  m_followsPublished = true;
151 ✔
853
  // Rebinds ports published after setup, e.g. restored by undo during playback
854
  if(auto tree = context.doc.findPlugin<LocalTree::ScriptableTreeBase>())
151 ✔
855
    connect(tree, &LocalTree::ScriptableTreeBase::published, this, [this](QObject* object) {
212 ✔
856
      QTimer::singleShot(0, this, [this, object = QPointer<QObject>{object}] {
122 ✔
857
        if(object)
61 ✔
858
          rebind(object.data());
61 ✔
859
      });
61 ✔
860
    });
61 ✔
861
}
4,142 ✔
862

863
void SetupContext::rebind(QObject* port)
88 ✔
864
{
865
  if(context.retired)
88 ✔
866
    return;
×
867
  if(auto in = qobject_cast<Process::Inlet*>(port))
88 ✔
868
  {
869
    if(auto it = inlets.find(in); it != inlets.end())
69 ✔
870
      set_destination_impl(
69 ✔
871
          context, effectiveAddress(*in), it->second.second, it->second.first,
69 ✔
872
          enqueue_in_context(*this));
69 ✔
873
  }
69 ✔
874
  else if(auto out = qobject_cast<Process::Outlet*>(port))
19 ✔
875
  {
876
    if(auto it = outlets.find(out); it != outlets.end())
3 ✔
877
      set_destination_impl(
3 ✔
878
          context, effectiveAddress(*out), it->second.second, it->second.first,
3 ✔
879
          enqueue_in_context(*this));
3 ✔
880
  }
3 ✔
881
}
88 ✔
882

883
SetupContext::~SetupContext() { }
4,916 ✔
884

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