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

ossia / score / 38268703700

12 Oct 2026 03:13PM UTC coverage: 48.723%. First build
38268703700

Pull #2302

github

web-flow
Merge 08e951e4e into 4b2f7f45e
Pull Request #2302: Mixer: a console of strips with meters, and the execution reporting back without stopping the audio

1430 of 2241 new or added lines in 48 files covered. (63.81%)

159878 of 328135 relevant lines covered (48.72%)

145109.87 hits per line

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

65.31
/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,779 ✔
35
  {
36
    static_assert(std::is_nothrow_move_constructible_v<F>);
37
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
3,779 ✔
38
    self.context.executionQueue.enqueue(std::move(f));
3,779 ✔
39
  }
3,779 ✔
40
};
41
struct VectorEnqueuer
42
{
43
  Transaction& vec;
44
  template <typename F>
45
  void operator()(F&& f)
2,091 ✔
46
  {
47
    static_assert(std::is_nothrow_move_constructible_v<F>);
48
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
2,091 ✔
49
    vec.push_back(std::move(f));
2,091 ✔
50
  }
2,091 ✔
51
};
52

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

59
static auto enqueue_in_vector(Transaction& vec) noexcept
1,061 ✔
60
{
61
  return VectorEnqueuer{vec};
1,061 ✔
62
}
63
}
64
ossia::net::node_base*
65
findNode(const ossia::execution_state& st, const State::Address& addr)
3,589 ✔
66
{
67
  auto& devs = st.edit_devices();
3,589 ✔
68
  auto dev_p = ossia::find_if(
3,589 ✔
69
      devs, [d = addr.device.toStdString()](auto& dev) { return dev->get_name() == d; });
7,684 ✔
70
  if(dev_p == devs.end())
3,589 ✔
71
    return nullptr;
17 ✔
72
  return ossia::net::find_node(
3,572 ✔
73
      (*dev_p)->get_root_node(), addr.path.join("/").toStdString());
3,572 ✔
74
}
3,589 ✔
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,236 ✔
183
    const Process::Port& proc_port, const T& port, Impl&& append)
184
{
185
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
4,236 ✔
186
  const ossia::unit_t& u = proc_port.unit().get();
4,236 ✔
187
  if(!u)
4,236 ✔
188
    return;
4,219 ✔
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,236 ✔
201

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

210
template <typename Port_T, typename OssiaPort_T, typename Impl>
211
void SetupContext::bind_address(
4,236 ✔
212
    Port_T& proc_port, const OssiaPort_T& ossia_port,
213
    RegisteredPorts::connections& cons, Impl&& impl)
214
{
215
  for(auto& con : cons)
4,236 ✔
216
    QObject::disconnect(con);
×
217
  cons.clear();
4,236 ✔
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,264 ✔
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,236 ✔
227
  // Queued: the namespace updates the published address on these signals too
228
  cons.push_back(connect(
8,472 ✔
229
      &proc_port, &Process::Port::scriptableChanged, this, rebind, Qt::QueuedConnection));
4,236 ✔
230
  cons.push_back(connect(
8,472 ✔
231
      &proc_port, &Process::Port::exposedChanged, this, rebind, Qt::QueuedConnection));
4,236 ✔
232
  if(auto proc = Process::parentProcess(&proc_port))
4,236 ✔
233
    cons.push_back(connect(
7,192 ✔
234
        proc, &Process::ProcessModel::scriptableChanged, this, rebind,
3,596 ✔
235
        Qt::QueuedConnection));
236
  follow_published();
4,236 ✔
237
  set_declared_unit_impl(proc_port, ossia_port, impl);
4,236 ✔
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,236 ✔
241
}
4,236 ✔
242

243
template <typename Impl>
244
void SetupContext::register_inlet_impl(
3,354 ✔
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,354 ✔
249
  SCORE_ASSERT(node);
3,354 ✔
250
  SCORE_ASSERT(ossia_port);
3,354 ✔
251

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

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

257
template <typename Impl>
258
void SetupContext::register_node_impl(
1,094 ✔
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,094 ✔
263
  if(node)
1,094 ✔
264
  {
265
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
1,094 ✔
266
    exec([wg, node = node]() mutable {
2,188 ✔
267
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
1,094 ✔
268
      if(auto g = wg.lock())
2,188 ✔
269
        g->add_node(std::move(node));
1,094 ✔
270
    });
1,094 ✔
271

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

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

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

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

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

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

301
void SetupContext::unregister_node(
420 ✔
302
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node)
303
{
304
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
420 ✔
305
  unregister_node(proc.inlets(), proc.outlets(), node);
420 ✔
306
  proc_map.erase(node.get());
420 ✔
307
}
420 ✔
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,308 ✔
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,308 ✔
333
  auto& s = plug.execState;
4,308 ✔
334
  auto& g = plug.execGraph;
4,308 ✔
335
  if(!g)
4,308 ✔
336
    return;
×
337

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

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

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

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

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

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

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

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

517
void SetupContext::on_cableCreated(Process::Cable& c)
81 ✔
518
{
519
  connectCable(c);
81 ✔
520
}
81 ✔
521

522
void SetupContext::on_cableRemoved(const Process::Cable& c)
86 ✔
523
{
524
  removeCable(c);
86 ✔
525
}
86 ✔
526

527
void SetupContext::connectCable(Process::Cable& c)
81 ✔
528
{
529
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
81 ✔
530
  connect_cable_impl(c, enqueue_in_context(*this));
81 ✔
531
}
81 ✔
532

533
void SetupContext::connectCable(Process::Cable& c, Transaction& vec)
36 ✔
534
{
535
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
36 ✔
536
  connect_cable_impl(c, enqueue_in_vector(vec));
36 ✔
537
}
36 ✔
538

539
void SetupContext::removeCable(const Process::Cable& c)
86 ✔
540
{
541
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
86 ✔
542
  disconnect_cable_impl(c, enqueue_in_context(*this));
86 ✔
543
}
86 ✔
544

545
void SetupContext::removeCable(const Process::Cable& c, Transaction& vec)
16 ✔
546
{
547
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
16 ✔
548
  disconnect_cable_impl(c, enqueue_in_vector(vec));
16 ✔
549
}
16 ✔
550

551
// What a process sends goes through its outlet's gain, pan and upmix, which
552
// follow the model.
553
template <typename Impl>
554
void SetupContext::register_mixing_impl(
389 ✔
555
    Process::AudioOutlet& proc_port, ossia::audio_outlet& out,
556
    const std::shared_ptr<ossia::graph_node>& node, Impl&& impl)
557
{
558
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
389 ✔
559
  auto* o = &out;
389 ✔
560
  const auto weights = proc_port.pan();
389 ✔
561
  impl([node, o, g = proc_port.gain(),
778 ✔
562
        pan = ossia::pan_weight(weights.begin(), weights.end()),
389 ✔
563
        mode = proc_port.upmixMode(), chans = proc_port.upmixChannels()]() mutable {
778 ✔
564
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
389 ✔
565
    o->gain = g;
389 ✔
566
    // A swap: the previous weights are freed with this closure, off the audio
567
    // thread.
568
    std::swap(o->pan, pan);
389 ✔
569
    o->upmix = ossia::audio_outlet::upmix_mode(mode);
389 ✔
570
    o->upmix_channels = chans;
389 ✔
571
  });
389 ✔
572

573
  auto& ports = runtime_connections[node];
389 ✔
574
  ports.disconnectMixing(proc_port.id());
389 ✔
575
  auto& cons = ports.mixing[proc_port.id()];
389 ✔
576
  auto& q = context.executionQueue;
389 ✔
577
  cons[0] = connect(&proc_port, &Process::AudioOutlet::gainChanged, this, [&q, node, o](double g) {
390 ✔
578
    q.enqueue([node, o, g] { o->gain = g; });
2 ✔
579
  });
1 ✔
580
  cons[1] = connect(
389 ✔
581
      &proc_port, &Process::AudioOutlet::panChanged, this,
389 ✔
582
      [&q, node, o](const auto& w) {
390 ✔
583
    q.enqueue([node, o, pan = ossia::pan_weight(w.begin(), w.end())]() mutable {
2 ✔
584
      std::swap(o->pan, pan);
1 ✔
585
    });
1 ✔
586
  });
1 ✔
587
  auto upmix = [&q, node, o, port = &proc_port] {
391 ✔
588
    q.enqueue([node, o, mode = port->upmixMode(), chans = port->upmixChannels()] {
4 ✔
589
      o->upmix = ossia::audio_outlet::upmix_mode(mode);
2 ✔
590
      o->upmix_channels = chans;
2 ✔
591
    });
2 ✔
592
  };
2 ✔
593
  cons[2] = connect(&proc_port, &Process::AudioOutlet::upmixModeChanged, this, upmix);
389 ✔
594
  cons[3] = connect(&proc_port, &Process::AudioOutlet::upmixChannelsChanged, this, upmix);
389 ✔
595
}
389 ✔
596

597
template <typename Impl>
598
void SetupContext::register_outlet_impl(
882 ✔
599
    Process::Outlet& proc_port, const ossia::outlet_ptr& ossia_port,
600
    const std::shared_ptr<ossia::graph_node>& node, Impl&& impl)
601
{
602
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
882 ✔
603
  SCORE_ASSERT(node);
882 ✔
604
  SCORE_ASSERT(ossia_port);
882 ✔
605
  bind_address(
882 ✔
606
      proc_port, ossia_port, runtime_connections[node].outlets[proc_port.id()], impl);
882 ✔
607

608
  if(auto audio = qobject_cast<Process::AudioOutlet*>(&proc_port);
882 ✔
609
     audio && ossia_port->which() == ossia::audio_port::which)
882 ✔
610
    register_mixing_impl(
389 ✔
611
        *audio, *static_cast<ossia::audio_outlet*>(ossia_port), node, impl);
389 ✔
612

613
  outlets.insert({&proc_port, std::make_pair(node, ossia_port)});
882 ✔
614

615
  proc_port.mapExecution(
1,764 ✔
616
      *ossia_port, [&](Process::Inlet& model_inl, ossia::inlet& ossia_inl) {
1,660 ✔
617
        register_inlet_impl(model_inl, &ossia_inl, node, impl);
778 ✔
618
      });
778 ✔
619

620
  // Unneeded : the execution_state only needs inlets to be registered,
621
  // in order to set up data value queues from the network thread
622

623
  // std::weak_ptr<ossia::execution_state> ws = context.execState;
624
  // impl([ws, ossia_port] {
625
  //   if (auto state = ws.lock())
626
  //     state->register_outlet(*ossia_port);
627
  // });
628
}
882 ✔
629

630
void SetupContext::unregister_inlet(
×
631
    const Process::Inlet& proc_port, const std::shared_ptr<ossia::graph_node>& node)
632
{
633
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
634
  if(node)
×
635
  {
636
    auto& runtime_connection = runtime_connections[node].inlets;
×
637
    auto it = runtime_connection.find(proc_port.id());
×
638
    if(it != runtime_connection.end())
×
639
    {
640
      for(auto& con : it->second)
×
641
        QObject::disconnect(con);
×
642
      runtime_connection.erase(it);
×
643
    }
×
644

645
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
646
    if(ossia_port_it != inlets.end())
×
647
    {
648
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
649
      context.executionQueue.enqueue([ws, ossia_port = ossia_port_it->second.second] {
×
650
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
651
        if(auto state = ws.lock())
×
652
          state->unregister_port(*ossia_port);
×
653
      });
×
654

655
      inlets.erase(ossia_port_it);
×
656
    }
×
657
  }
×
658
  else
659
  {
660
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
661
  }
662
}
×
663

664
void SetupContext::unregister_outlet(
×
665
    const Process::Outlet& proc_port, const std::shared_ptr<ossia::graph_node>& node)
666
{
667
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
668
  if(node)
×
669
  {
670
    auto& runtime_connection = runtime_connections[node].outlets;
×
671
    auto it = runtime_connection.find(proc_port.id());
×
672
    if(it != runtime_connection.end())
×
673
    {
674
      for(auto& con : it->second)
×
675
        QObject::disconnect(con);
×
676
      runtime_connection.erase(it);
×
677
    }
×
NEW
678
    runtime_connections[node].disconnectMixing(proc_port.id());
×
679

680
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
681
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
682
      unregister_inlet(model_inl, node);
×
683
    });
×
684
  }
×
685

686
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
687
}
×
688

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

705
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
706
    if(ossia_port_it != inlets.end())
×
707
    {
708
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
709
      commands.push_back([ws, ossia_port = ossia_port_it->second.second] {
×
710
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
711
        if(auto state = ws.lock())
×
712
          state->unregister_port(*ossia_port);
×
713
      });
×
714

715
      inlets.erase(ossia_port_it);
×
716
    }
×
717
  }
×
718
  else
719
  {
720
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
721
  }
722
}
×
723

724
void SetupContext::unregister_outlet(
×
725
    const Process::Outlet& proc_port, const std::shared_ptr<ossia::graph_node>& node,
726
    Transaction& commands)
727
{
728
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
729
  if(node)
×
730
  {
731
    auto& runtime_connection = runtime_connections[node].outlets;
×
732
    auto it = runtime_connection.find(proc_port.id());
×
733
    if(it != runtime_connection.end())
×
734
    {
735
      for(auto& con : it->second)
×
736
        QObject::disconnect(con);
×
737
      runtime_connection.erase(it);
×
738
    }
×
NEW
739
    runtime_connections[node].disconnectMixing(proc_port.id());
×
740

741
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
742
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
743
      unregister_inlet(model_inl, node);
×
744
    });
×
745
  }
×
746

747
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
748
}
×
749

750
void SetupContext::replace_node(
×
751
    const std::shared_ptr<ossia::time_process>& process,
752
    const std::shared_ptr<ossia::graph_node>& node, Transaction& commands)
753
{
754
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
755
  commands.push_back([p = process, n = node]() mutable {
×
756
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
757
    using namespace std;
758
    swap(p->node, n);
×
759
  });
×
760
}
×
761

762
void SetupContext::unregister_node(
789 ✔
763
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
764
    const std::shared_ptr<ossia::graph_node>& node)
765
{
766
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
789 ✔
767
  if(node)
789 ✔
768
  {
769
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
789 ✔
770
    std::weak_ptr<ossia::execution_state> ws = context.execState;
789 ✔
771
    context.executionQueue.enqueue([wg, ws, node = node] mutable {
1,232 ✔
772
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
443 ✔
773
      if(auto s = ws.lock())
886 ✔
774
      {
775
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
1,647 ✔
776
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
852 ✔
777
      }
443 ✔
778

779
      if(auto g = wg.lock())
886 ✔
780
        g->remove_node(node);
443 ✔
781

782
      node->clear();
443 ✔
783
    });
443 ✔
784

785
    runtime_connections[node].clear();
789 ✔
786
    runtime_connections.erase(node);
789 ✔
787

788
    proc_map.erase(node.get());
789 ✔
789
  }
789 ✔
790

791
  for(auto ptr : proc_inlets)
2,585 ✔
792
    inlets.erase(ptr);
1,796 ✔
793
  for(auto ptr : proc_outlets)
1,461 ✔
794
    outlets.erase(ptr);
672 ✔
795
}
789 ✔
796

797
void SetupContext::unregister_node(
×
798
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
799
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
800
{
801
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
802
  if(node)
×
803
  {
804
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
×
805
    std::weak_ptr<ossia::execution_state> ws = context.execState;
×
806
    vec.push_back([wg, ws, node] {
×
807
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
808
      if(auto s = ws.lock())
×
809
      {
810
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
×
811
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
×
812
      }
×
813

814
      if(auto g = wg.lock())
×
815
        g->remove_node(node);
×
816
      node->clear();
×
817
    });
×
818

819
    runtime_connections[node].clear();
×
820
    runtime_connections.erase(node);
×
821

822
    proc_map.erase(node.get());
×
823
  }
×
824

825
  for(auto ptr : proc_inlets)
×
826
    inlets.erase(ptr);
×
827
  for(auto ptr : proc_outlets)
×
828
    outlets.erase(ptr);
×
829
}
×
830

831
void SetupContext::unregister_node_soft(
78 ✔
832
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
833
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
834
{
835
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
78 ✔
836
  if(node)
78 ✔
837
  {
838
    std::weak_ptr<ossia::execution_state> ws = context.execState;
78 ✔
839
    vec.push_back([ws, node] {
155 ✔
840
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
77 ✔
841
      if(auto s = ws.lock())
154 ✔
842
      {
843
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
790 ✔
844
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
184 ✔
845
      }
77 ✔
846
    });
77 ✔
847
    runtime_connections[node].clear();
78 ✔
848
    runtime_connections.erase(node);
78 ✔
849

850
    proc_map.erase(node.get());
78 ✔
851
  }
78 ✔
852

853
  for(auto ptr : proc_inlets)
758 ✔
854
    inlets.erase(ptr);
680 ✔
855
  for(auto ptr : proc_outlets)
189 ✔
856
    outlets.erase(ptr);
111 ✔
857
}
78 ✔
858

859
SetupContext::SetupContext(Context& other) noexcept
4,877 ✔
860
    : context{other}
4,877 ✔
861
{
9,754 ✔
862
}
863

864
void SetupContext::follow_published()
4,236 ✔
865
{
866
  if(m_followsPublished)
4,236 ✔
867
    return;
4,074 ✔
868
  m_followsPublished = true;
162 ✔
869
  // Rebinds ports published after setup, e.g. restored by undo during playback
870
  if(auto tree = context.doc.findPlugin<LocalTree::ScriptableTreeBase>())
162 ✔
871
    connect(tree, &LocalTree::ScriptableTreeBase::published, this, [this](QObject* object) {
223 ✔
872
      QTimer::singleShot(0, this, [this, object = QPointer<QObject>{object}] {
122 ✔
873
        if(object)
61 ✔
874
          rebind(object.data());
61 ✔
875
      });
61 ✔
876
    });
61 ✔
877
}
4,236 ✔
878

879
void SetupContext::rebind(QObject* port)
88 ✔
880
{
881
  if(auto in = qobject_cast<Process::Inlet*>(port))
88 ✔
882
  {
883
    if(auto it = inlets.find(in); it != inlets.end())
69 ✔
884
      set_destination(effectiveAddress(*in), it->second.second);
69 ✔
885
  }
69 ✔
886
  else if(auto out = qobject_cast<Process::Outlet*>(port))
19 ✔
887
  {
888
    if(auto it = outlets.find(out); it != outlets.end())
3 ✔
889
      set_destination(effectiveAddress(*out), it->second.second);
3 ✔
890
  }
3 ✔
891
}
88 ✔
892

893
SetupContext::~SetupContext() { }
4,877 ✔
894

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