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

ossia / score / 30161608849

25 Jul 2026 02:25PM UTC coverage: 15.303% (-0.008%) from 15.311%
30161608849

Pull #2148

github

web-flow
Merge d2b82a62f into 13afd939a
Pull Request #2148: wasm: stream large media from Blob URLs instead of copying into RAM (Tier B)

4 of 179 new or added lines in 10 files covered. (2.23%)

7 existing lines in 5 files now uncovered.

30396 of 198627 relevant lines covered (15.3%)

983.52 hits per line

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

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

3
#include <Media/RMSData.hpp>
4
#include <Media/Sound/QImagePool.hpp>
5
#include <Media/Sound/SoundModel.hpp>
6

7
#include <score/tools/ThreadPool.hpp>
8
#include <score/tools/std/Invoke.hpp>
9

10
#include <ossia/detail/ssize.hpp>
11

12
#include <QGraphicsView>
13
#include <QScrollBar>
14
#include <QTimer>
15

16
namespace Media::Sound
17
{
18
LayerView::LayerView(const ProcessModel& m, QGraphicsItem* parent)
×
19
    : Process::LayerView{parent}
×
20
    , m_cpt{new WaveformComputer{}}
21
    , m_model{m}
22
{
×
23
  setCacheMode(NoCache);
×
24
  setFlag(ItemClipsToShape, true);
×
25
  this->setAcceptDrops(true);
×
26

27
  if(auto view = getView(*parent))
×
28
  {
29
    connect(
×
30
        view->horizontalScrollBar(), &QScrollBar::valueChanged, this,
×
31
        &Media::Sound::LayerView::scrollValueChanged);
32
  }
×
33
  connect(
×
34
      m_cpt, &WaveformComputer::ready, this,
×
35
      [this](QVector<QImage*> img, ComputedWaveform wf) {
×
36
    {
37
      QImagePool::instance().giveBack(m_images);
×
38
      m_images = std::move(img);
×
39

40
      // We display the image at the device ratio of the view
41
      if(auto view = ::getView(*this))
×
42
      {
43
        for(auto image : m_images)
×
44
        {
45
          image->setDevicePixelRatio(view->devicePixelRatioF());
×
46
        }
47
      }
×
48
    }
49
    m_wf = wf;
×
50

51
    update();
×
52
      });
×
53
  QTimer::singleShot(10, this, [this] { recompute(); });
×
54
}
×
55

56
LayerView::~LayerView()
×
57
{
×
58
  m_cpt->stop();
×
59

60
  ossia::qt::run_async(m_cpt, &QObject::deleteLater);
×
61
  m_cpt = nullptr;
×
62

63
  score::ThreadPool::instance().releaseThread();
×
64

65
  QImagePool::instance().giveBack(m_images);
×
66
}
×
67

68
void LayerView::setData(const std::shared_ptr<AudioFile>& data)
×
69
{
70
  if(m_data)
×
71
  {
72
    QObject::disconnect(&m_data->rms(), nullptr, this, nullptr);
×
73
    m_data->on_finishedDecoding.disconnect<&LayerView::on_finishedDecoding>(*this);
×
74
  }
×
75

76
  SCORE_ASSERT(data);
×
77

78
  m_data = data;
×
79
  m_numChan = data->channels();
×
80
  if(m_data)
×
81
  {
82
    connect(
×
83
        &m_data->rms(), &RMSData::finishedDecoding, this,
×
84
        &LayerView::on_finishedDecoding, Qt::QueuedConnection);
85
    connect(
×
86
        &m_data->rms(), &RMSData::newData, this, &LayerView::on_newData,
×
87
        Qt::QueuedConnection);
88
    m_data->on_finishedDecoding.connect<&LayerView::on_finishedDecoding>(*this);
×
89
    on_newData();
×
90
  }
×
91
  m_sampleRate = data->sampleRate();
×
92
}
×
93

94
void LayerView::recompute() const
×
95
{
96
  if(Q_UNLIKELY(
×
97
         !m_data || width() < 2. || height() < 2. || m_zoom <= 0.
98
         || m_model.file()->sampleRate() < 1.))
99
    return;
×
100

101
#if defined(__EMSCRIPTEN__)
102
  // The waveform is computed on a worker thread. A weblocalfile: source is a
103
  // browser Blob bound to the main thread, so decoding it off-thread is
104
  // undefined behaviour; skip the waveform for such (streamed) sources.
105
  if(m_model.file()->absoluteFileName().startsWith(QLatin1String("weblocalfile:")))
106
    return;
107
#endif
108

UNCOV
109
  if(auto view = getView(*this))
×
110
  {
111
    // By default we try to force a render of everything, but it's too slow with very large files
112
    double minutes
×
113
        = double(m_model.file()->decodedSamples()) / m_model.file()->sampleRate();
×
114
    if(minutes > 10)
×
115
      m_renderAll = false;
×
116

117
    // On the first render we render the whole thing
118
    double x0 = m_renderAll ? 0 : mapFromScene(view->mapToScene(0, 0)).x();
×
119
    double xf
×
120
        = m_renderAll ? 100000 : mapFromScene(view->mapToScene(view->width(), 0)).x();
×
121

122
    WaveformRequest req{
×
123
        m_data,
×
124
        m_zoom,
×
125
        m_tempoRatio,
×
126
        QSizeF{width(), height()},
×
127
        view->devicePixelRatioF(),
×
128
        x0,
×
129
        xf,
×
130
        m_model.startOffset(),
×
131
        m_model.loopDuration(),
×
132
        m_model.loops(),
×
133
        m_frontColors};
×
134
    m_cpt->recompute(std::move(req));
×
135
    m_recomputed = true;
×
136
    m_renderAll = false;
×
137
  }
×
138
}
×
139

140
void LayerView::setFrontColors(bool b)
×
141
{
142
  if(b != m_frontColors)
×
143
  {
144
    m_frontColors = b;
×
145
    recompute();
×
146
  }
×
147
}
×
148

149
void LayerView::setTempoRatio(double r)
×
150
{
151
  if(r != m_tempoRatio)
×
152
  {
153
    m_tempoRatio = r;
×
154
    recompute();
×
155
  }
×
156
}
×
157

158
void LayerView::recompute(ZoomRatio ratio)
×
159
{
160
  m_zoom = ratio;
×
161
  recompute();
×
162
}
×
163

164
void LayerView::paint_impl(QPainter* painter) const
×
165
{
166
  if(m_zoom == 0.)
×
167
    return;
×
168
  if(!m_data)
×
169
    return;
×
170

171
  int channels = std::ssize(m_images);
×
172
  if(channels == 0.)
×
173
  {
174
    if(!m_recomputed)
×
175
    {
176
      m_renderAll = true;
×
177
      recompute();
×
178
    }
×
179
    return;
×
180
  }
181

182
  auto ratio = m_wf.zoom / m_zoom;
×
183

184
  const qreal w = (m_wf.xf - m_wf.x0) * ratio;
×
185
  if(w < 2.)
×
186
    return;
×
187

188
  const qreal h = height() / channels;
×
189
  if(h < 2.)
×
190
    return;
×
191

192
  const double x0 = m_wf.x0 * ratio;
×
193

194
  painter->setRenderHint(QPainter::SmoothPixmapTransform, 0);
×
195
  for(int i = 0; i < channels; i++)
×
196
  {
197
    painter->drawImage(QRectF{x0, h * i, w, h}, *m_images[i]);
×
198
  }
×
199
  painter->setRenderHint(QPainter::SmoothPixmapTransform, 1);
×
200
}
×
201

202
void LayerView::scrollValueChanged(int sbvalue)
×
203
{
204
  // TODO maybe we don't actually need to always recompute... check if we're in
205
  // the visible area.
206
  // TODO on_heightChanged
207
  recompute();
×
208
}
×
209

210
void LayerView::on_finishedDecoding()
×
211
{
212
  recompute();
×
213
}
×
214

215
void LayerView::on_newData()
×
216
{
217
  recompute();
×
218
}
×
219

220
void LayerView::mousePressEvent(QGraphicsSceneMouseEvent* ev)
×
221
{
222
  pressed(ev->scenePos());
×
223
  ev->ignore();
×
224
}
×
225

226
void LayerView::dragEnterEvent(QGraphicsSceneDragDropEvent* event)
×
227
{
228
  event->accept();
×
229
}
×
230

231
void LayerView::dragLeaveEvent(QGraphicsSceneDragDropEvent* event)
×
232
{
233
  event->accept();
×
234
}
×
235

236
void LayerView::dragMoveEvent(QGraphicsSceneDragDropEvent* event)
×
237
{
238
  event->accept();
×
239
}
×
240

241
void LayerView::dropEvent(QGraphicsSceneDragDropEvent* event)
×
242
{
243
  event->accept();
×
244

245
  if(event->mimeData())
×
246
    dropReceived(event->pos(), *event->mimeData());
×
247
}
×
248

249
void LayerView::heightChanged(qreal r)
×
250
{
251
  Process::LayerView::heightChanged(r);
×
252
  m_renderAll = true;
×
253
  recompute();
×
254
}
×
255

256
void LayerView::widthChanged(qreal w)
×
257
{
258
  Process::LayerView::widthChanged(w);
×
259
  m_renderAll = true;
×
260
  recompute();
×
261
}
×
262
}
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