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

ossia / score / 32923660642

26 Aug 2026 02:40AM UTC coverage: 23.574% (+0.4%) from 23.21%
32923660642

push

github

jcelerier
gfx: add missing guards on message processing for texture / buffer / geometry

0 of 6 new or added lines in 1 file covered. (0.0%)

4413 existing lines in 83 files now uncovered.

53181 of 225592 relevant lines covered (23.57%)

61000.63 hits per line

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

25.84
/src/plugins/score-plugin-engine/Execution/ExecutionController.cpp
1
#include "ExecutionController.hpp"
2

3
#include <Explorer/DocumentPlugin/DeviceDocumentPlugin.hpp>
4
#include <Explorer/Explorer/DeviceExplorerModel.hpp>
5
#include <Explorer/Explorer/DeviceExplorerWidget.hpp>
6

7
#include <Scenario/Application/ScenarioActions.hpp>
8
#include <Scenario/Application/ScenarioApplicationPlugin.hpp>
9
#include <Scenario/Document/BaseScenario/BaseScenario.hpp>
10
#include <Scenario/Document/Interval/IntervalExecution.hpp>
11
#include <Scenario/Document/ScenarioDocument/ScenarioDocumentModel.hpp>
12
#include <Scenario/Document/ScenarioDocument/ScenarioDocumentPresenter.hpp>
13
#include <Scenario/Execution/score2OSSIA.hpp>
14
#include <Scenario/Process/ScenarioExecution.hpp>
15

16
#include <Audio/AudioApplicationPlugin.hpp>
17
#include <Audio/Settings/Model.hpp>
18
#include <Execution/Clock/ClockFactory.hpp>
19
#include <Execution/DocumentPlugin.hpp>
20
#include <Execution/Settings/ExecutorModel.hpp>
21

22
#include <score/actions/ActionManager.hpp>
23
#include <score/model/ComponentUtils.hpp>
24
#include <score/tools/Bind.hpp>
25
#include <score/widgets/MessageBox.hpp>
26

27
#include <core/application/ApplicationSettings.hpp>
28
#include <core/document/Document.hpp>
29
#include <core/presenter/DocumentManager.hpp>
30

31
#include <ossia/dataflow/execution_state.hpp>
32
#include <ossia/editor/scenario/time_interval.hpp>
33
#include <ossia/editor/state/state.hpp>
34

35
#include <QGuiApplication>
36
#include <QMainWindow>
37

38
#include <Transport/DocumentPlugin.hpp>
39
#include <Transport/TransportInterface.hpp>
40

41
/**
42
 * Execution state-machine explanation:
43
 *
44
 * * There are multiple sources for what controls the transport:
45
 * - From the GUI
46
 * - From the local tree or remote control plug-in
47
 * - From an external software (e.g. JACK transport)
48
 * - Automatically (e.g. --autoplay argument on command-line)
49
 *
50
 * * No matter the transport source, if there's a GUI its state must be consistent
51
 *   with what happens.
52
 *
53
 * Thus:
54
 * * The do_*** functions are requests
55
 * * The trigger_*** functions will perform the action and set the GUI state correctly
56
 * * The on_*** contain the actual implementation of the transport operation
57
 *
58
 * Notes on QAction:
59
 * * QAction::toggle(); -> will send toggled(true/false);
60
 * * QAction::trigger(); -> will send toggled(true/false); triggered(true/false);
61
 * * QAction::check(b); -> will send toggled(b);
62
 * * GUI: like QAction::trigger
63
 *
64
 * Thus, triggered can be used to differentiate between
65
 * * called from the GUI
66
 * * called from the software
67
 *
68
 * Example of call sequence:
69
 *
70
 * * Pressing the "global play button" (stop -> play):
71
 *   -> QAction::toggled(true)
72
 *     -> GUI changes (see TransportActions ctor)
73
 *   -> QAction::triggered(true)
74
 *     -> ApplicationPlugin::request_play_global(true)
75
 *     -> TransportInterface::requestPlay()
76
 *   -> the transport implementation eventually sends the "play()" signal
77
 *     -> &ApplicationPlugin::trigger_play_global
78
 *
79
 * * Pressing the "global play button" (play -> pause):
80
 *   -> QAction::toggled(false)
81
 *     -> GUI changes (see TransportActions ctor)
82
 *   -> QAction::triggered(false)
83
 *     -> ApplicationPlugin::request_play_global(false)
84
 *     -> TransportInterface::requestPause()
85
 *   -> the transport implementation eventually sends the "pause()" signal
86
 *     -> &ApplicationPlugin::trigger_pause()
87
 *
88
 * * Playing with "play from here":
89
 *   -> ApplicationPlugin::request_play(true, t)
90
 *   -> TransportInterface::requestTransport(t)
91
 *   -> TransportInterface::requestPlay
92
 *   -> The transport implementation eventually sends the "transport()" signal
93
 *     -> ??
94
 *   -> The transport implementation eventually sends the "play()" signal
95
 *     -> &ApplicationPlugin::trigger_play_global
96
 *
97
 * * Starting the transport from QJackCtl with a GUI
98
 *   -> the transport implementation eventually sends the "play()" signal
99
 *     -> &ApplicationPlugin::trigger_play_global
100
 *
101
 * * Starting the transport from QJackCtl without a GUI
102
 *   -> the transport implementation eventually sends the "play()" signal
103
 *     -> &ApplicationPlugin::trigger_play_global
104
 *
105
 * * Receiving a "/play" message to the local tree with a GUI
106
 *   -> ApplicationPlugin::request_play
107
 *   -> TransportInterface::requestPlay
108
 *   -> The transport implementation eventually sends the "play()" signal
109
 *     -> &ApplicationPlugin::trigger_play_global
110
 *
111
 * * Receiving a "/play" message to the local tree without a GUI
112
 */
113

114
namespace Execution
115
{
116
ExecutionController::ExecutionController(const score::GUIApplicationContext& ctx)
524✔
117
    : context{ctx}
262✔
118
    , m_scenario{ctx.guiApplicationPlugin<Scenario::ScenarioApplicationPlugin>()}
262✔
119
    , m_actions{m_scenario.transportActions()}
262✔
120
{
262✔
121
  if(ctx.applicationSettings.gui)
262✔
122
  {
123
    auto& acts = ctx.actions;
256✔
124
    using namespace Actions;
125
    connect(
256✔
126
        acts.action<Play>().action(), &QAction::triggered, this,
256✔
127
        &ExecutionController::request_play_local, Qt::QueuedConnection);
128

129
    connect(
256✔
130
        acts.action<PlayGlobal>().action(), &QAction::triggered, this,
256✔
131
        &ExecutionController::request_play_global, Qt::QueuedConnection);
132

133
    connect(
256✔
134
        acts.action<Stop>().action(), &QAction::triggered, this,
256✔
135
        &ExecutionController::request_stop, Qt::QueuedConnection);
136

137
    connect(
256✔
138
        acts.action<Reinitialize>().action(), &QAction::triggered, this,
256✔
139
        &ExecutionController::trigger_reinitialize, Qt::QueuedConnection);
140

141
    connect(
256✔
142
        &m_scenario.execution(), &Scenario::ScenarioExecution::playAtDate, this,
256✔
143
        &ExecutionController::request_play_from_here);
144
    connect(
256✔
145
        &m_scenario.execution(), &Scenario::ScenarioExecution::beginScrub, this,
256✔
146
        &ExecutionController::request_begin_scrub);
147
    connect(
256✔
148
        &m_scenario.execution(), &Scenario::ScenarioExecution::scrub, this,
256✔
149
        &ExecutionController::request_scrub);
150
    connect(
256✔
151
        &m_scenario.execution(), &Scenario::ScenarioExecution::endScrub, this,
256✔
152
        &ExecutionController::request_end_scrub);
153
  }
256✔
154
}
×
155

156
ExecutionController::~ExecutionController()
261✔
157
{
261✔
158
  if(m_transport)
261✔
159
    m_transport->teardown();
261✔
160
}
261✔
161

162
TransportInterface& ExecutionController::transport() const noexcept
×
163
{
164
  return *m_transport;
×
165
}
166

167
void ExecutionController::request_play_global(bool b)
×
168
{
169
  if(b)
×
170
  {
171
    this->m_requestLocalPlay = false;
×
172
    m_transport->requestPlay();
×
173
  }
×
174
  else
175
  {
176
    m_transport->requestPause();
×
177
  }
178
}
×
179

180
void ExecutionController::request_play_local(bool b)
×
181
{
182
  if(b)
×
183
  {
184
    this->m_requestLocalPlay = true;
×
185
    m_transport->requestPlay();
×
186
  }
×
187
  else
188
  {
189
    m_transport->requestPause();
×
190
  }
191
}
×
192

193
void ExecutionController::request_play_interval(
×
194
    Scenario::IntervalModel& itv, exec_setup_fun setup, TimeVal t)
195
{
196
  m_intervalsToPlay.push_back({itv, std::move(setup), t});
×
197
  m_transport->requestPlay();
×
198
}
×
199

200
void ExecutionController::request_stop_interval(Scenario::IntervalModel& itv)
×
201
{
202
  stop_interval(itv);
×
203
}
×
204

205
void ExecutionController::request_stop()
510✔
206
{
207
  m_transport->requestStop();
510✔
208
}
510✔
209

210
void ExecutionController::trigger_play()
×
211
{
212
  if(!m_intervalsToPlay.empty())
×
213
  {
214
    m_actions.onPlayLocal();
×
215
    for(auto& to_play : m_intervalsToPlay)
×
216
      play_interval(to_play.interval, std::move(to_play.setup), to_play.t);
×
217
    m_intervalsToPlay.clear();
×
218
  }
×
219
  else if(this->m_requestLocalPlay)
×
220
  {
221
    m_actions.onPlayLocal();
×
222
    on_play_local(true);
×
223
  }
×
224
  else
225
  {
226
    m_actions.onPlayGlobal();
×
227
    on_play_global(true);
×
228
  }
229

230
  // Note: we set this here so that external "play" request are global
231
  // as it makes more sense:
232
  // local play is just for checking a sub-part of the score, but the external tools's times will
233
  // always be relative to the whole general score
234
  this->m_requestLocalPlay = false;
×
235
}
×
236

237
void ExecutionController::trigger_pause()
×
238
{
239
  m_actions.onPause();
×
240
  on_pause();
×
241
}
×
242

243
void ExecutionController::trigger_stop()
510✔
244
{
245
  m_actions.onStop();
510✔
246
  on_stop();
510✔
247

248
  // See note above
249
  this->m_requestLocalPlay = false;
510✔
250
}
510✔
251

252
void ExecutionController::trigger_reinitialize()
×
253
{
254
  m_actions.onStop();
×
255
  on_reinitialize();
×
256

257
  // See note above
258
  this->m_requestLocalPlay = false;
×
259
}
×
260

261
void ExecutionController::on_play_global(bool b)
×
262
{
263
  if(auto scenar = currentScenarioModel())
×
264
  {
265
    if(b)
×
266
    {
267
      play_interval(scenar->baseInterval(), {}, TimeVal::zero());
×
268
    }
×
269
    else
270
    {
271
      on_pause();
×
272
    }
273
  }
×
274
}
×
275

276
void ExecutionController::on_play_local(bool b, ::TimeVal t)
×
277
{
278
  if(auto scenar = currentScenarioPresenter())
×
279
  {
280
    if(b)
×
281
    {
282
      std::optional<TimeVal> startTime;
×
283
      auto& itv = scenar->displayedInterval();
×
284
      {
285
        if(itv.startMarker() != TimeVal::zero())
×
286
          startTime = itv.startMarker();
×
287
      }
288
      if(!startTime)
×
289
        if(t != TimeVal::zero())
×
290
          startTime = t;
×
291

292
      if(startTime && m_playing && m_clock)
×
293
      {
294
        if(m_clock->paused())
×
295
        {
296
          on_transport(*startTime);
×
297
        }
×
298
      }
×
299
      play_interval(scenar->displayedInterval(), {}, startTime ? *startTime : t);
×
300
    }
×
301
    else
302
    {
303
      on_pause();
×
304
    }
305
  }
×
306
  else
307
  {
308
    on_play_global(b);
×
309
  }
310
}
×
311

312
void ExecutionController::on_play_local(bool b)
×
313
{
314
  on_play_local(b, ::TimeVal::zero());
×
315
}
×
316

317
void ExecutionController::on_pause()
×
318
{
319
  ensure_audio_engine();
×
320

321
  if(m_clock)
×
322
  {
323
    m_clock->pause();
×
324
    m_paused = true;
×
325

326
    if(auto doc = currentDocument())
×
327
    {
328
      doc->context().execTimer.stop();
×
329

330
      if(auto transport_plug = doc->context().findPlugin<Transport::DocumentPlugin>())
×
331
        transport_plug->pause();
×
332
    }
×
333
  }
×
334
}
×
335

336
void ExecutionController::on_transport(TimeVal t)
×
337
{
338
  if(!m_clock)
×
339
    return;
×
340

341
  SCORE_ASSERT(m_clock->scenario);
×
342
  auto itv = m_clock->scenario->baseInterval().OSSIAInterval();
×
343
  if(!itv)
×
344
    return;
×
345

346
  auto& settings = context.settings<Execution::Settings::Model>();
×
347
  auto& ctx = m_clock->context;
×
348
  if(settings.getTransportValueCompilation())
×
349
  {
350
    auto execState = m_clock->context.execState;
×
351
    ctx.executionQueue.enqueue([execState, itv, time = m_clock->context.time(t)] {
×
352
      itv->offset(time);
×
353
      execState->commit();
×
354
    });
×
355
  }
×
356
  else
357
  {
358
    ctx.executionQueue.enqueue(
×
359
        [itv, time = m_clock->context.time(t)] { itv->transport(time); });
×
360
  }
361

362
  if(auto doc = currentDocument())
×
363
    if(auto transport_plug = doc->context().findPlugin<Transport::DocumentPlugin>())
×
364
      transport_plug->transport(t);
×
365
}
×
366

367
void ExecutionController::on_begin_scrub(TimeVal t)
×
368
{
369
  on_transport(t);
×
370
}
×
371

372
void ExecutionController::on_scrub(TimeVal t)
×
373
{
374
  on_transport(t);
×
375
}
×
376

377
void ExecutionController::on_end_scrub(TimeVal t)
×
378
{
379
  on_transport(t);
×
380
}
×
381

382
void ExecutionController::request_play_from_localtree(bool val)
×
383
{
384
  if(!m_playing && val)
×
385
  {
386
    // not playing, play requested
387
    request_play_local(val);
×
388
  }
×
389
  else if(m_playing)
×
390
  {
391
    if(m_paused == val)
×
392
    {
393
      // paused, play requested
394
      // or playing, pause requested
395
      request_play_local(val);
×
396
    }
×
397
  }
×
398
}
×
399

400
void ExecutionController::request_play_global_from_localtree(bool val)
×
401
{
402
  if(!m_playing && val)
×
403
  {
404
    // not playing, play requested
405
    request_play_global(val);
×
406
  }
×
407
  else if(m_playing)
×
408
  {
409
    if(m_paused == val)
×
410
    {
411
      // paused, play requested
412
      // or playing, pause requested
413
      request_play_global(val);
×
414
    }
×
415
  }
×
416
}
×
417

418
void ExecutionController::request_transport_from_localtree(TimeVal t)
×
419
{
420
  m_transport->requestTransport(t);
×
421
}
×
422

423
void ExecutionController::request_stop_from_localtree()
×
424
{
425
  trigger_stop();
×
426
}
×
427

428
void ExecutionController::request_reinitialize_from_localtree()
×
429
{
430
  trigger_reinitialize();
×
431
}
×
432

433
void ExecutionController::request_play_from_here(TimeVal t)
×
434
{
435
  if(m_clock)
×
436
  {
437
    m_transport->requestTransport(t);
×
438
  }
×
439
  else
440
  {
441
    on_play_local(true, t);
×
442

443
    // FIXME this ends up calling play_interval again...
444
    auto act = this->context.actions.action<Actions::Play>().action();
×
445
    act->trigger();
×
446
  }
447
}
×
448

449
void ExecutionController::request_begin_scrub(TimeVal t)
×
450
{
451
  if(m_clock)
×
452
  {
453
    m_transport->requestBeginScrub(t);
×
454
  }
×
455
  else
456
  {
457
    on_play_local(true, t);
×
458

459
    // FIXME this ends up calling play_interval again...
460
    auto act = this->context.actions.action<Actions::Play>().action();
×
461
    act->trigger();
×
462
  }
463
}
×
464

465
void ExecutionController::request_scrub(TimeVal t)
×
466
{
467
  if(m_clock)
×
468
  {
469
    m_transport->requestScrub(t);
×
470
  }
×
471
}
×
472

473
void ExecutionController::request_end_scrub(TimeVal t)
×
474
{
475
  if(m_clock)
×
476
  {
477
    m_transport->requestEndScrub(t);
×
478
  }
×
479
}
×
480

481
void ExecutionController::ensure_audio_engine()
×
482
{
483
  auto& audio_engine = this->context.guiApplicationPlugin<Audio::ApplicationPlugin>();
×
484
  if(!audio_engine.audio)
×
485
  {
486
    if(this->context.mainWindow)
×
487
    {
488
      score::warning(
×
489
          this->context.mainWindow, tr("Cannot play"),
×
490
          tr("Cannot start playback. It looks like the audio engine is not "
×
491
             "running.\n"
492
             "Check the audio settings in the software settings to ensure "
493
             "that a sound card "
494
             "is correctly configured.\n\n"
495
             "Check Settings > Audio > Device in particular. "
496
             "The power-on icon at the bottom of the transport toolbar will "
497
             "light up when the engine is running."));
498
      return;
×
499
    }
500
    else
501
    {
502
      qFatal("Cannot playback without an audio engine set up");
×
503
    }
504
  }
505
}
×
506

507
void ExecutionController::play_interval(
×
508
    Scenario::IntervalModel& cst, exec_setup_fun setup_fun, TimeVal t)
509
{
510
  auto doc = currentDocument();
×
511
  if(!doc)
×
512
    return;
×
513

514
  auto& ctx = doc->context();
×
515

516
  auto exec_plug = ctx.findPlugin<Execution::DocumentPlugin>();
×
517
  if(!exec_plug)
×
518
    return;
×
519

520
  ensure_audio_engine();
×
521

522
  if(m_playing)
×
523
  {
524
    SCORE_ASSERT(bool(m_clock));
×
525
    if(m_clock->paused())
×
526
    {
527
      m_clock->resume();
×
528
      m_paused = false;
×
529
    }
×
530
    else
531
    {
532
      // We are playing an interval while we are already executing.
533
      if(auto scenar = qobject_cast<Scenario::ProcessModel*>(cst.parent()))
×
534
      {
535
        auto exec_comp = score::findComponent<Execution::ScenarioComponentBase>(
×
536
            scenar->components());
×
537
        if(exec_comp)
×
538
        {
539
          exec_comp->playInterval(cst);
×
540
        }
×
541
        return;
×
542
      }
543
    }
544
  }
×
545
  else
546
  {
547
    // Here we stop the listening when we start playing the scenario.
548
    // Get all the selected nodes
549
    if(auto explorer = Explorer::try_deviceExplorerFromObject(*doc))
×
550
    {
551
      // Disable listening for everything
552
      if(explorer && !exec_plug->settings.getExecutionListening())
×
553
      {
554
        explorer->deviceModel().listening().stop();
×
555
      }
×
556

557
      if(this->context.applicationSettings.gui)
×
558
      {
559
        if(auto w = Explorer::findDeviceExplorerWidgetInstance(this->context))
×
560
        {
561
          w->setEditable(false);
×
562
        }
×
563
      }
×
564
    }
×
565

566
    // Ignores the top-level condition / triggers if any
567
    bool forcePlay{};
×
568
    if(this->context.applicationSettings.gui)
×
569
    {
570
      forcePlay = qApp->keyboardModifiers() & Qt::ControlModifier;
×
571
    }
×
572
    exec_plug->reload(forcePlay, cst);
×
573

574
    auto& c = exec_plug->context();
×
575
    m_clock = makeClock(c);
×
576

577
    if(setup_fun)
×
578
    {
579
      exec_plug->runAllCommands();
×
580
      SCORE_ASSERT(exec_plug->baseScenario());
×
581
      setup_fun(c, *exec_plug->baseScenario());
×
582
      exec_plug->runAllCommands();
×
583
    }
×
584

585
    m_clock->play(t);
×
586
    m_paused = false;
×
587
  }
588

589
  m_playing = true;
×
590
  ctx.execTimer.start();
×
591

592
  if(auto transport_plug = ctx.findPlugin<Transport::DocumentPlugin>())
×
593
    transport_plug->play();
×
594
}
×
595

596
void ExecutionController::stop_interval(Scenario::IntervalModel& cst)
×
597
{
598
  auto doc = currentDocument();
×
599
  if(!doc)
×
600
    return;
×
601

602
  auto& ctx = doc->context();
×
603

604
  auto exec_plug = ctx.findPlugin<Execution::DocumentPlugin>();
×
605
  if(!exec_plug)
×
606
    return;
×
607

608
  ensure_audio_engine();
×
609

610
  if(m_playing)
×
611
  {
612
    // We are stopping an interval while we are already executing.
613
    if(auto scenar = qobject_cast<Scenario::ProcessModel*>(cst.parent()))
×
614
    {
615
      auto exec_comp
×
616
          = score::findComponent<Execution::ScenarioComponentBase>(scenar->components());
×
617
      if(exec_comp)
×
618
      {
619
        exec_comp->stopInterval(cst);
×
620
      }
×
621
    }
×
622
  }
×
623
}
×
624

625
TimeVal ExecutionController::execution_time() const
1,123✔
626
{
627
  if(m_clock)
1,123✔
628
  {
629
    SCORE_ASSERT(m_clock->scenario);
×
630
    auto& itv = m_clock->scenario->baseInterval().scoreInterval().duration;
×
631
    return TimeVal(itv.defaultDuration() * itv.playPercentage());
×
632
  }
633
  return TimeVal::zero();
1,123✔
634
}
1,123✔
635

636
void ExecutionController::on_record(::TimeVal t)
×
637
{
638
  SCORE_ASSERT(!m_playing);
×
639

640
  // TODO have a on_exit handler to properly stop the scenario.
641
  if(auto scenar = currentScenarioModel())
×
642
  {
643
    if(auto exec_plug = scenar->context().findPlugin<Execution::DocumentPlugin>())
×
644
    {
645
      // Listening isn't stopped here.
646
      exec_plug->reload(false, scenar->baseInterval());
×
647
      m_clock = makeClock(exec_plug->context());
×
648
      m_clock->play(t);
×
649

650
      scenar->context().execTimer.start();
×
651
      m_playing = true;
×
652
      m_paused = false;
×
653

654
      if(auto transport_plug = scenar->context().findPlugin<Transport::DocumentPlugin>())
×
655
        transport_plug->play();
×
656
    }
×
657
  }
×
658
}
×
659

660
void ExecutionController::on_stop()
510✔
661
{
662
  bool wasplaying = m_playing;
510✔
663
  m_playing = false;
510✔
664
  m_paused = false;
510✔
665

666
  stop_clock();
510✔
667

668
  if(wasplaying)
510✔
669
    send_end_state();
×
670

671
  reset_after_stop();
510✔
672
}
510✔
673

674
void ExecutionController::stop_clock()
510✔
675
{
676
  if(m_clock)
510✔
677
  {
678
    auto clock = std::move(m_clock);
×
679
    m_clock.reset();
×
680
    if(auto scenar = currentScenarioModel())
×
681
      if(auto exec_plug = scenar->context().findPlugin<Execution::DocumentPlugin>();
×
682
         bool(exec_plug))
×
683
      {
684
        try
685
        {
686
          clock->stop();
×
687
        }
×
688
        catch(...)
689
        {
690
          qDebug() << "Error while stopping the clock. There is likely an audio "
×
691
                      "hardware issue.";
692
        }
×
693
      }
×
694
  }
×
695
}
510✔
696

697
void ExecutionController::send_end_state()
×
698
{
699
  if(auto doc = currentDocument())
×
700
  {
701
    auto& ctx = doc->context();
×
702
    if(auto exec_plug = ctx.findPlugin<Execution::DocumentPlugin>())
×
703
    {
704
      exec_plug->playStopState();
×
705
    }
×
706
  }
×
707
}
×
708
void ExecutionController::reset_after_stop()
510✔
709
{
710
  if(auto doc = currentDocument())
510✔
711
  {
712
    auto& ctx = doc->context();
357✔
713
    ctx.execTimer.stop();
357✔
714

715
    if(auto transport_plug = ctx.findPlugin<Transport::DocumentPlugin>())
357✔
716
      transport_plug->stop();
357✔
717

718
    if(auto exec_plug = ctx.findPlugin<Execution::DocumentPlugin>())
357✔
719
    {
720
      exec_plug->clear();
357✔
721
    }
357✔
722
    else
723
    {
724
      return;
×
725
    }
726

727
    // If we can we resume listening
728
    if(!context.docManager.preparingNewDocument())
357✔
729
    {
730
      auto explorer = Explorer::try_deviceExplorerFromObject(*doc);
246✔
731
      if(explorer)
246✔
732
        explorer->deviceModel().listening().restore();
246✔
733
    }
246✔
734
  }
357✔
735

736
  auto scenar = currentScenarioModel();
510✔
737
  if(!scenar || scenar->closing())
510✔
738
    return;
220✔
739
  QTimer::singleShot(
290✔
740
      50, this,
741
      [this, itv = QPointer<Scenario::IntervalModel>{&scenar->baseInterval()}] {
580✔
742
    if(itv)
290✔
743
      reset_edition(*itv);
101✔
744
      });
290✔
745
}
510✔
746

747
void ExecutionController::reset_edition(Scenario::IntervalModel& base)
101✔
748
{
749
  // Reset edition for the device explorer
750
  if(context.applicationSettings.gui)
101✔
751
  {
752
    if(auto w = Explorer::findDeviceExplorerWidgetInstance(context))
101✔
753
    {
754
      w->setEditable(true);
79✔
755
    }
79✔
756
  }
101✔
757

758
  // FIXME uuuugh have an event in scenario instead (or better, move this
759
  // in process)
760
  base.reset();
101✔
761
  base.executionEvent(Scenario::IntervalExecutionEvent::Finished);
101✔
762
}
101✔
763

UNCOV
764
void ExecutionController::on_reinitialize()
×
765
{
766
  // TODO to be more precise, we should stop the execution, but keep
767
  // the execution_state alive until the last message is sent
UNCOV
768
  if(auto scenar = currentScenarioModel())
×
769
  {
UNCOV
770
    auto& ctx = scenar->context();
×
771
    auto& doc = ctx.document;
×
UNCOV
772
    auto exec_plug = ctx.findPlugin<Execution::DocumentPlugin>();
×
773
    if(!exec_plug)
×
774
      return;
×
775

776
    auto explorer = Explorer::try_deviceExplorerFromObject(doc);
×
777

778
    // Disable listening for everything
779
    if(explorer)
×
UNCOV
780
      if(!ctx.app.settings<Execution::Settings::Model>().getExecutionListening())
×
UNCOV
781
        explorer->deviceModel().listening().stop();
×
782

783
    // If we can we resume listening
784
    if(explorer)
×
UNCOV
785
      if(!context.docManager.preparingNewDocument())
×
UNCOV
786
        explorer->deviceModel().listening().restore();
×
787

788
    on_stop();
×
789

UNCOV
790
    exec_plug->playStartState();
×
791
  }
×
UNCOV
792
}
×
793

794
void ExecutionController::init_transport()
262✔
795
{
796
  if(m_transport)
262✔
UNCOV
797
    m_transport->teardown();
×
798

799
  auto& s = context.settings<Execution::Settings::Model>();
262✔
800
  m_transport = s.getTransport();
262✔
801
  SCORE_ASSERT(m_transport);
262✔
802
  m_transport->setup();
262✔
803
  connect(
262✔
804
      m_transport, &Execution::TransportInterface::play, this,
262✔
805
      &ExecutionController::trigger_play);
806
  connect(
262✔
807
      m_transport, &Execution::TransportInterface::pause, this,
262✔
808
      &ExecutionController::trigger_pause);
809
  connect(
262✔
810
      m_transport, &Execution::TransportInterface::stop, this,
262✔
811
      &ExecutionController::trigger_stop);
812
  connect(
262✔
813
      m_transport, &Execution::TransportInterface::transport, this,
262✔
814
      &ExecutionController::on_transport);
815
  connect(
262✔
816
      m_transport, &Execution::TransportInterface::beginScrub, this,
262✔
817
      &ExecutionController::on_begin_scrub);
818
  connect(
262✔
819
      m_transport, &Execution::TransportInterface::scrub, this,
262✔
820
      &ExecutionController::on_scrub);
821
  connect(
262✔
822
      m_transport, &Execution::TransportInterface::endScrub, this,
262✔
823
      &ExecutionController::on_end_scrub);
824

825
  auto& audio_settings = this->context.settings<Audio::Settings::Model>();
262✔
826
  con(audio_settings, &Audio::Settings::Model::JackTransportChanged, this,
524✔
827
      &ExecutionController::init_transport, Qt::UniqueConnection);
262✔
828
}
262✔
829

830
Scenario::ScenarioDocumentModel* ExecutionController::currentScenarioModel()
510✔
831
{
832
  if(auto doc = currentDocument())
510✔
833
  {
834
    return score::IDocument::try_modelDelegate<Scenario::ScenarioDocumentModel>(*doc);
357✔
835
  }
836
  return nullptr;
153✔
837
}
510✔
838

UNCOV
839
Scenario::ScenarioDocumentPresenter* ExecutionController::currentScenarioPresenter()
×
840
{
UNCOV
841
  if(auto doc = currentDocument())
×
842
  {
UNCOV
843
    return score::IDocument::try_presenterDelegate<Scenario::ScenarioDocumentPresenter>(
×
844
        *doc);
×
845
  }
846
  return nullptr;
×
847
}
×
848

849
score::Document* ExecutionController::currentDocument() const
1,020✔
850
{
851
  return context.documents.currentDocument();
1,020✔
852
}
853

854
std::unique_ptr<Execution::Clock>
UNCOV
855
ExecutionController::makeClock(const Execution::Context& ctx)
×
856
{
UNCOV
857
  auto& s = context.settings<Execution::Settings::Model>();
×
858
  auto clk = s.makeClock(ctx);
×
UNCOV
859
  SCORE_ASSERT(clk->scenario);
×
860
  auto& itv = clk->scenario->baseInterval().interval();
×
861
  con(itv, &IdentifiedObjectAbstract::identified_object_destroying, this,
×
862
      [this] { trigger_stop(); });
×
863
  return clk;
×
864
}
×
865

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