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

ossia / score / 30754311710

02 Aug 2026 03:25PM UTC coverage: 15.222% (-0.04%) from 15.263%
30754311710

Pull #2163

github

web-flow
Merge 8b7bf0aad into e99b6a5da
Pull Request #2163: Fix backwards playback for audio plug-ins (VST, VST3, LV2, JSFX)

0 of 57 new or added lines in 5 files covered. (0.0%)

343 existing lines in 16 files now uncovered.

30381 of 199582 relevant lines covered (15.22%)

1075.26 hits per line

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

0.0
/src/plugins/score-plugin-media/Media/Metro/MetroExecutor.cpp
1
#include "MetroExecutor.hpp"
2

3
#include <Process/ExecutionContext.hpp>
4

5
#include <Explorer/DocumentPlugin/DeviceDocumentPlugin.hpp>
6

7
#include <Scenario/Execution/score2OSSIA.hpp>
8

9
#include <Library/LibrarySettings.hpp>
10
#include <Media/MediaFileHandle.hpp>
11

12
#include <score/application/ApplicationContext.hpp>
13
#include <score/tools/Bind.hpp>
14

15
#include <ossia/dataflow/graph_node.hpp>
16
#include <ossia/dataflow/port.hpp>
17
#include <ossia/detail/pod_vector.hpp>
18
#include <ossia/editor/scenario/time_value.hpp>
19

20
#include <QCoreApplication>
21

22
namespace ossia::nodes
23
{
24
class audio_metronome final : public ossia::nonowning_graph_node
25
{
26
public:
27
  const ossia::small_vector<audio_sample*, 8>& hi_sound;
28
  const ossia::small_vector<audio_sample*, 8>& lo_sound;
29
  const int64_t hi_dur{}, lo_dur{};
30

31
  ossia::audio_outlet audio_out;
32
  ossia::value_outlet bang_out;
33

34
  struct played_sound
35
  {
36
    const ossia::small_vector<audio_sample*, 8>* samples{};
37
    int64_t pos{};
38
    int64_t dur{};
39
    int64_t start_sample{};
40
    int64_t fade_total{};
41
    int64_t fade_remaining{};
42
  };
43

44
  ossia::small_vector<played_sound, 8> in_flight;
45

46
  audio_metronome(
×
47
      const ossia::small_vector<audio_sample*, 8>& hi,
48
      const ossia::small_vector<audio_sample*, 8>& lo, int64_t hi_dur, int64_t lo_dur)
49
      : hi_sound{hi}
×
50
      , lo_sound{lo}
×
51
      , hi_dur{hi_dur}
×
52
      , lo_dur{lo_dur}
×
53
  {
×
54
    m_outlets.push_back(&audio_out);
×
55
    m_outlets.push_back(&bang_out);
×
56
  }
×
57
  std::string label() const noexcept override { return "audio_metronome"; }
×
58

59
  void run(const token_request& tk, exec_state_facade st) noexcept override
×
60
  {
NEW
61
    if(!tk.paused())
×
62
    {
63
      tk.metronome(
×
64
          st.modelToSamples(),
×
65
          [&](int64_t hi_start_sample) {
×
66
        for(auto& sound : in_flight)
×
67
        {
68
          sound.fade_total = std::min((int64_t)500, sound.dur - sound.pos);
×
69
          sound.fade_remaining = sound.fade_total;
×
70
        }
71

72
        if(hi_dur > 0)
×
73
        {
74
          bang_out.target<value_port>()->write_value(ossia::impulse{}, hi_start_sample);
×
75
          in_flight.push_back({&hi_sound, 0, hi_dur, hi_start_sample, 0, 0});
×
76
        }
×
77
          },
×
78
          [&](int64_t lo_start_sample) {
×
79
        for(auto& sound : in_flight)
×
80
        {
81
          sound.fade_total = std::min((int64_t)500, sound.dur - sound.pos);
×
82
          sound.fade_remaining = sound.fade_total;
×
83
        }
84

85
        if(lo_dur > 0)
×
86
        {
87
          bang_out.target<value_port>()->write_value(ossia::impulse{}, lo_start_sample);
×
88
          in_flight.push_back({&lo_sound, 0, lo_dur, lo_start_sample, 0, 0});
×
89
        }
×
90
      });
×
91
    }
×
92

93
    audio_out.target<audio_port>()->set_channels(2);
×
94
    auto& ap = audio_out.target<audio_port>()->get();
×
95

96
    auto render_sound = [&ap, &tk, &st](
×
97
                            const int64_t fade_total, int64_t& fade_remaining,
98
                            int64_t& pos, const int64_t dur, const int64_t start_sample,
99
                            const ossia::small_vector<audio_sample*, 8>& sound) {
100
      bool finished = false;
×
101

102
      ap.resize(2);
×
103
      ap[0].resize(st.bufferSize());
×
104
      ap[1].resize(st.bufferSize());
×
105

106
      const float* const src = sound[0] + pos;
×
107

108
      const auto [tick_start, d] = st.timings(tk);
×
109

110
      int64_t count = d - start_sample;
×
111
      if(count < 0)
×
112
        return true;
×
113
      if(pos + count < dur)
×
114
      {
115
        pos += count;
×
116
      }
×
117
      else
118
      {
119
        count = dur - pos;
×
120
        finished = true;
×
121
      }
122

123
      auto fade_remaining_prev = fade_remaining;
×
124
      for(auto dst : {ap[0].data(), ap[1].data()})
×
125
      {
126
        fade_remaining = fade_remaining_prev;
×
127
        double* start = dst + tick_start + start_sample;
×
128
        for(int i = 0; i < count; i++)
×
129
        {
130
          const double fade
×
131
              = (fade_total == 0 ? 1. : double(--fade_remaining) / fade_total);
×
132

133
          start[i] += src[i] * fade;
×
134
        }
×
135
      }
136

137
      finished |= fade_total > 0 && fade_remaining <= 0;
×
138

139
      return finished;
×
140
    };
×
141

142
    for(auto it = in_flight.begin(); it != in_flight.end();)
×
143
    {
144
      bool finished = render_sound(
×
145
          it->fade_total, it->fade_remaining, it->pos, it->dur, it->start_sample,
×
146
          *it->samples);
×
147

148
      if(it->start_sample != 0)
×
149
        it->start_sample = 0;
×
150

151
      if(finished)
×
152
      {
153
        it = in_flight.erase(it);
×
154
      }
×
155
      else
156
      {
157
        ++it;
×
158
      }
159
    }
160
  }
×
161
};
162
}
163

164
namespace Execution
165
{
166
struct MetronomeSounds
×
167
{
168
  const QString root
×
169
      = score::AppContext().settings<Library::Settings::Model>().getDefaultLibraryPath()
×
170
        + "/Util/";
×
171
  const std::unique_ptr<Media::AudioFile> tick{[this] {
×
172
    auto f = std::make_unique<Media::AudioFile>();
×
173
    f->load(
×
174
        {root + "/metro_tick.wav", root + "/metro_tick.wav",
×
175
         Media::DecodingMethod::Libav});
176

177
    while(f->samples() != f->decodedSamples())
178
    {
179
      std::this_thread::sleep_for(std::chrono::milliseconds(1));
×
180
      QCoreApplication::instance()->processEvents();
×
181
    }
182
    return f;
183
  }()};
×
184
  const std::unique_ptr<Media::AudioFile> tock{[this] {
×
185
    auto f = std::make_unique<Media::AudioFile>();
×
186
    f->load(
×
187
        {root + "/metro_tock.wav", root + "/metro_tock.wav",
×
188
         Media::DecodingMethod::Libav});
189

190
    while(f->samples() != f->decodedSamples())
191
    {
192
      std::this_thread::sleep_for(std::chrono::milliseconds(1));
×
193
      QCoreApplication::instance()->processEvents();
×
194
    }
195
    return f;
196
  }()};
×
197

198
  const Media::AudioFile::ViewHandle tick_handle{tick->handle()};
×
199
  const Media::AudioFile::ViewHandle tock_handle{tock->handle()};
×
200

201
  operator bool() const noexcept
×
202
  {
203
    return tick_handle.target<Media::AudioFile::RAMView>()
×
204
           && tock_handle.target<Media::AudioFile::RAMView>();
×
205
  }
206
};
207

208
MetroComponent::MetroComponent(
×
209
    Media::Metro::Model& element, const Execution::Context& ctx, QObject* parent)
210
    : Execution::ProcessComponent_T<Media::Metro::Model, ossia::node_process>{
×
211
        element, ctx, "Executor::MetroComponent", parent}
×
212
{
×
213
  static const MetronomeSounds sounds;
×
214
  if(sounds)
×
215
  {
216
    const auto& tick_sound{sounds.tick_handle.target<Media::AudioFile::RAMView>()->data};
×
217
    const auto& tock_sound{sounds.tock_handle.target<Media::AudioFile::RAMView>()->data};
×
218

219
    auto node = ossia::make_node<ossia::nodes::audio_metronome>(
×
220
        *ctx.execState.get(), tick_sound, tock_sound, sounds.tick->decodedSamples(),
×
221
        sounds.tock->decodedSamples());
×
222

223
    this->node = node;
×
224
    m_ossia_process = std::make_shared<ossia::node_process>(node);
×
225
  }
×
226
}
×
227

228
void MetroComponent::recompute() { }
×
229

230
MetroComponent::~MetroComponent() { }
×
231
}
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