• 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

2.19
/src/plugins/score-plugin-media/Media/AudioDecoder.cpp
1
#include "AudioDecoder.hpp"
2

3
#include <Media/AvStreamIO.hpp>
4
#include <Media/Libav.hpp>
5
#include <Media/Sound/SoundModel.hpp>
6

7
#include <score/tools/Debug.hpp>
8

9
#include <ossia/dataflow/sample_to_float.hpp>
10
#include <ossia/detail/libav.hpp>
11
#include <ossia/detail/variant.hpp>
12

13
#include <QHash>
14

15
#include <cmath>
16

17
#include <cstdio>
18
#include <cstdlib>
19
#include <cstring>
20

21
#if SCORE_HAS_LIBAV
22
#include <Media/LibavMediaInfo.hpp>
23
extern "C" {
24
#include <libavcodec/avcodec.h>
25
#include <libavformat/avformat.h>
26
#include <libavutil/frame.h>
27
#include <libavutil/mem.h>
28
#include <libswresample/swresample.h>
29
}
30
#endif
31

32
#include <wobjectimpl.h>
33
W_OBJECT_IMPL(Media::AudioDecoder)
1✔
34
#if SCORE_HAS_LIBAV
35
namespace
36
{
37
using namespace Media;
38
static const constexpr std::size_t dynamic_channels
39
    = std::numeric_limits<std::size_t>::max();
40

41
template <
42
    typename SampleFormat, std::size_t Channels, std::size_t SampleSize, bool Planar>
43
struct Decoder;
44

45
template <typename SampleFormat, std::size_t SampleSize>
46
struct Decoder<SampleFormat, 1, SampleSize, true>
47
{
48
  void operator()(audio_array& data, std::size_t curpos, uint8_t** buf, std::size_t n)
×
49
  {
50
    auto dat = reinterpret_cast<SampleFormat*>(buf[0]);
×
51
    for(std::size_t j = 0; j < n; j++)
×
52
    {
53
      data[0][curpos + j] = ossia::sample_to_float<SampleFormat, SampleSize>(dat[j]);
×
54
    }
×
55
  }
×
56
};
57

58
template <typename SampleFormat, std::size_t Channels, std::size_t SampleSize>
59
struct Decoder<SampleFormat, Channels, SampleSize, false>
60
{
61
  void operator()(audio_array& data, std::size_t curpos, uint8_t** buf, std::size_t n)
×
62
  {
63
    auto dat = reinterpret_cast<SampleFormat*>(buf[0]);
×
64

65
    int i = 0;
×
66
    for(std::size_t j = 0; j < Channels * n;)
×
67
    {
68
      std::size_t cur_j = curpos + i;
×
69
      for(std::size_t chan = 0; chan < Channels; chan++)
×
70
      {
71
        data[chan][cur_j] = ossia::sample_to_float<SampleFormat, SampleSize>(dat[j]);
×
72
        j++;
×
73
      }
×
74
      i++;
×
75
    }
76
  }
×
77
};
78

79
template <typename SampleFormat, std::size_t Channels, std::size_t SampleSize>
80
struct Decoder<SampleFormat, Channels, SampleSize, true>
81
{
82
  void operator()(audio_array& data, std::size_t curpos, uint8_t** buf, std::size_t n)
×
83
  {
84
    auto dat = reinterpret_cast<SampleFormat**>(buf);
×
85
    for(std::size_t chan = 0; chan < Channels; chan++)
×
86
    {
87
      for(std::size_t j = 0; j < n; j++)
×
88
      {
89
        data[chan][curpos + j]
×
90
            = ossia::sample_to_float<SampleFormat, SampleSize>(dat[chan][j]);
×
91
      }
×
92
    }
×
93
  }
×
94
};
95

96
template <typename SampleFormat, std::size_t SampleSize>
97
struct Decoder<SampleFormat, dynamic_channels, SampleSize, false>
98
{
99
  std::size_t Channels{};
100
  void operator()(audio_array& data, std::size_t curpos, uint8_t** buf, std::size_t n)
×
101
  {
102
    auto dat = reinterpret_cast<SampleFormat*>(buf[0]);
×
103

104
    int i = 0;
×
105
    for(std::size_t j = 0; j < Channels * n;)
×
106
    {
107
      std::size_t cur_j = curpos + i;
×
108
      for(std::size_t chan = 0; chan < Channels; chan++)
×
109
      {
110
        data[chan][cur_j] = ossia::sample_to_float<SampleFormat, SampleSize>(dat[j]);
×
111
        j++;
×
112
      }
×
113
      i++;
×
114
    }
115
  }
×
116
};
117

118
template <typename SampleFormat, std::size_t SampleSize>
119
struct Decoder<SampleFormat, dynamic_channels, SampleSize, true>
120
{
121
  std::size_t Channels{};
122
  void operator()(audio_array& data, std::size_t curpos, uint8_t** buf, std::size_t n)
×
123
  {
124
    auto dat = reinterpret_cast<SampleFormat**>(buf);
×
125
    for(std::size_t chan = 0; chan < Channels; chan++)
×
126
    {
127
      for(std::size_t j = 0; j < n; j++)
×
128
      {
129
        data[chan][curpos + j]
×
130
            = ossia::sample_to_float<SampleFormat, SampleSize>(dat[chan][j]);
×
131
      }
×
132
    }
×
133
  }
×
134
};
135

136
using decoder_t = ossia::variant<
137
    Decoder<int16_t, 1, 16, true>, Decoder<int16_t, 2, 16, true>,
138
    Decoder<int16_t, 2, 16, false>, /*
139
     Decoder<int16_t, 4, 16, true>,
140
     Decoder<int16_t, 4, 16, false>,
141
     Decoder<int16_t, 6, 16, true>,
142
     Decoder<int16_t, 6, 16, false>,
143
     Decoder<int16_t, 8, 16, true>,
144
     Decoder<int16_t, 8, 16, false>,*/
145
    Decoder<int16_t, dynamic_channels, 16, true>,
146
    Decoder<int16_t, dynamic_channels, 16, false>,
147

148
    Decoder<int32_t, 1, 24, true>, Decoder<int32_t, 2, 24, true>,
149
    Decoder<int32_t, 2, 24, false>, /*
150
     Decoder<int32_t, 4, 24, true>,
151
     Decoder<int32_t, 4, 24, false>,
152
     Decoder<int32_t, 6, 24, true>,
153
     Decoder<int32_t, 6, 24, false>,
154
     Decoder<int32_t, 8, 24, true>,
155
     Decoder<int32_t, 8, 24, false>,*/
156
    Decoder<int32_t, dynamic_channels, 24, true>,
157
    Decoder<int32_t, dynamic_channels, 24, false>,
158

159
    Decoder<int32_t, 1, 32, true>, Decoder<int32_t, 2, 32, true>,
160
    Decoder<int32_t, 2, 32, false>, /*
161
     Decoder<int32_t, 4, 32, true>,
162
     Decoder<int32_t, 4, 32, false>,
163
     Decoder<int32_t, 6, 32, true>,
164
     Decoder<int32_t, 6, 32, false>,
165
     Decoder<int32_t, 8, 32, true>,
166
     Decoder<int32_t, 8, 32, false>,*/
167
    Decoder<int32_t, dynamic_channels, 32, true>,
168
    Decoder<int32_t, dynamic_channels, 32, false>,
169

170
    Decoder<float, 1, 32, true>, Decoder<float, 2, 32, true>,
171
    Decoder<float, 2, 32, false>, /*
172
   Decoder<float,   4, 32, true>,
173
   Decoder<float,   4, 32, false>,
174
   Decoder<float,   6, 32, true>,
175
   Decoder<float,   6, 32, false>,
176
   Decoder<float,   8, 32, true>,
177
   Decoder<float,   8, 32, false>,*/
178
    Decoder<float, dynamic_channels, 32, true>,
179
    Decoder<float, dynamic_channels, 32, false>,
180

181
    Decoder<double, 1, 64, true>, Decoder<double, 2, 64, true>,
182
    Decoder<double, 2, 64, false>, /*
183
       Decoder<float,   4, 32, true>,
184
       Decoder<float,   4, 32, false>,
185
       Decoder<float,   6, 32, true>,
186
       Decoder<float,   6, 32, false>,
187
       Decoder<float,   8, 32, true>,
188
       Decoder<float,   8, 32, false>,*/
189
    Decoder<double, dynamic_channels, 64, true>,
190
    Decoder<double, dynamic_channels, 64, false>,
191

192
    Decoder<uint8_t, 1, 8, true>, Decoder<uint8_t, 2, 8, true>,
193
    Decoder<uint8_t, 2, 8, false>, Decoder<uint8_t, dynamic_channels, 8, true>,
194
    Decoder<uint8_t, dynamic_channels, 8, false>>;
195

196
template <std::size_t N>
197
decoder_t make_N_decoder(AVStream& stream)
×
198
{
199
  const int size = stream.codecpar->bits_per_raw_sample;
×
200

201
  if(size == 0 || size == 8 || size == 16 || size == 32)
×
202
  {
203
    switch((AVSampleFormat)stream.codecpar->format)
×
204
    {
205
      case AVSampleFormat::AV_SAMPLE_FMT_U8:
206
        return Decoder<uint8_t, N, 8, false>{};
×
207
      case AVSampleFormat::AV_SAMPLE_FMT_S16:
208
        return Decoder<int16_t, N, 16, false>{};
×
209
      case AVSampleFormat::AV_SAMPLE_FMT_S32:
210
        return Decoder<int32_t, N, 32, false>{};
×
211
      case AVSampleFormat::AV_SAMPLE_FMT_FLT:
212
        return Decoder<float, N, 32, false>{};
×
213

214
      case AVSampleFormat::AV_SAMPLE_FMT_U8P:
215
        return Decoder<uint8_t, N, 8, true>{};
×
216
      case AVSampleFormat::AV_SAMPLE_FMT_S16P:
217
        return Decoder<int16_t, N, 16, true>{};
×
218
      case AVSampleFormat::AV_SAMPLE_FMT_S32P:
219
        return Decoder<int32_t, N, 32, true>{};
×
220
      case AVSampleFormat::AV_SAMPLE_FMT_FLTP:
221
        return Decoder<float, N, 32, true>{};
×
222
      default:
223
        return {};
×
224
    }
225
  }
226
  else if(size == 24)
×
227
  {
228
    switch((AVSampleFormat)stream.codecpar->format)
×
229
    {
230
      case AVSampleFormat::AV_SAMPLE_FMT_S32:
231
        return Decoder<int32_t, N, 24, false>{};
×
232
      case AVSampleFormat::AV_SAMPLE_FMT_S32P:
233
        return Decoder<int32_t, N, 24, true>{};
×
234
      default:
235
        break;
×
236
    }
237
  }
×
238
  else if(size == 64)
×
239
  {
240
    switch((AVSampleFormat)stream.codecpar->format)
×
241
    {
242
      case AVSampleFormat::AV_SAMPLE_FMT_DBL:
243
        return Decoder<double, N, 64, false>{};
×
244
      case AVSampleFormat::AV_SAMPLE_FMT_DBLP:
245
        return Decoder<double, N, 64, true>{};
×
246
      default:
247
        return {};
×
248
    }
249
  }
250
  return {};
×
251
}
×
252

253
decoder_t make_dynamic_decoder(AVStream& stream)
×
254
{
255
  const int size = stream.codecpar->bits_per_raw_sample;
×
256
  const std::size_t channels = ossia::avstream_get_audio_channels(stream);
×
257

258
  if(size == 0 || size == 8 || size == 16 || size == 32)
×
259
  {
260
    switch((AVSampleFormat)stream.codecpar->format)
×
261
    {
262
      case AVSampleFormat::AV_SAMPLE_FMT_U8:
263
        return Decoder<uint8_t, dynamic_channels, 8, false>{channels};
×
264
      case AVSampleFormat::AV_SAMPLE_FMT_S16:
265
        return Decoder<int16_t, dynamic_channels, 16, false>{channels};
×
266
      case AVSampleFormat::AV_SAMPLE_FMT_S32:
267
        return Decoder<int32_t, dynamic_channels, 32, false>{channels};
×
268
      case AVSampleFormat::AV_SAMPLE_FMT_FLT:
269
        return Decoder<float, dynamic_channels, 32, false>{channels};
×
270

271
      case AVSampleFormat::AV_SAMPLE_FMT_U8P:
272
        return Decoder<uint8_t, dynamic_channels, 8, true>{channels};
×
273
      case AVSampleFormat::AV_SAMPLE_FMT_S16P:
274
        return Decoder<int16_t, dynamic_channels, 16, true>{channels};
×
275
      case AVSampleFormat::AV_SAMPLE_FMT_S32P:
276
        return Decoder<int32_t, dynamic_channels, 32, true>{channels};
×
277
      case AVSampleFormat::AV_SAMPLE_FMT_FLTP:
278
        return Decoder<float, dynamic_channels, 32, true>{channels};
×
279
      default:
280
        return {};
×
281
    }
282
  }
283
  else if(size == 24)
×
284
  {
285
    switch((AVSampleFormat)stream.codecpar->format)
×
286
    {
287
      case AVSampleFormat::AV_SAMPLE_FMT_S32:
288
        return Decoder<int32_t, dynamic_channels, 24, false>{channels};
×
289
      case AVSampleFormat::AV_SAMPLE_FMT_S32P:
290
        return Decoder<int32_t, dynamic_channels, 24, true>{channels};
×
291
      default:
292
        break;
×
293
    }
294
  }
×
295
  else if(size == 64)
×
296
  {
297
    switch((AVSampleFormat)stream.codecpar->format)
×
298
    {
299
      case AVSampleFormat::AV_SAMPLE_FMT_DBL:
300
        return Decoder<double, dynamic_channels, 64, false>{};
×
301
      case AVSampleFormat::AV_SAMPLE_FMT_DBLP:
302
        return Decoder<double, dynamic_channels, 64, true>{};
×
303
      default:
304
        return {};
×
305
    }
306
  }
307

308
  return {};
×
309
}
×
310

311
// Simple mono case: everything is planar
312
template <>
313
decoder_t make_N_decoder<1>(AVStream& stream)
×
314
{
315
  const int size = stream.codecpar->bits_per_raw_sample;
×
316

317
  if(size == 0 || size == 8 || size == 16 || size == 32)
×
318
  {
319
    switch((AVSampleFormat)stream.codecpar->format)
×
320
    {
321
      case AVSampleFormat::AV_SAMPLE_FMT_U8:
322
      case AVSampleFormat::AV_SAMPLE_FMT_U8P:
323
        return Decoder<uint8_t, 1, 8, true>{};
×
324
      case AVSampleFormat::AV_SAMPLE_FMT_S16:
325
      case AVSampleFormat::AV_SAMPLE_FMT_S16P:
326
        return Decoder<int16_t, 1, 16, true>{};
×
327
      case AVSampleFormat::AV_SAMPLE_FMT_S32:
328
      case AVSampleFormat::AV_SAMPLE_FMT_S32P:
329
        return Decoder<int32_t, 1, 32, true>{};
×
330
      case AVSampleFormat::AV_SAMPLE_FMT_FLT:
331
      case AVSampleFormat::AV_SAMPLE_FMT_FLTP:
332
        return Decoder<float, 1, 32, true>{};
×
333
      default:
334
        return {};
×
335
    }
336
  }
337
  else if(size == 24)
×
338
  {
339
    return Decoder<int32_t, 1, 24, true>{};
×
340
  }
341
  else if(size == 64)
×
342
  {
343
    return Decoder<double, 1, 64, true>{};
×
344
  }
345

346
  return {};
×
347
}
×
348

349
static decoder_t make_decoder(AVStream& stream)
×
350
{
351
  switch(ossia::avstream_get_audio_channels(stream))
×
352
  {
353
    case 1:
354
      return make_N_decoder<1>(stream);
×
355
    case 2:
356
      return make_N_decoder<2>(stream); /*
×
357
case 4: return make_N_decoder<4>(stream);
358
case 6: return make_N_decoder<6>(stream);
359
case 8: return make_N_decoder<8>(stream);*/
360
    case 0:
361
      throw std::runtime_error("Stream has no channels");
×
362
    default:
363
      return make_dynamic_decoder(stream);
×
364
  }
365
}
×
366
}
367
#endif
368

369
struct AVCodecContext;
370
struct AVFormatContext;
371
struct AVFrame;
372
namespace Media
373
{
374
AudioDecoder::AudioDecoder(int rate)
1✔
375
    : convertedSampleRate{rate}
376
{
1✔
377
  connect(
1✔
378
      this, &AudioDecoder::startDecode, this, &AudioDecoder::on_startDecode,
379
      Qt::QueuedConnection);
380
}
×
381

382
AudioDecoder::~AudioDecoder()
1✔
383
{
1✔
384
  m_decodeThread.exit();
1✔
385
  while(m_decodeThread.isRunning())
1✔
386
    ;
387
}
1✔
388

389
struct AVCodecContext_Free
390
{
391
  void operator()(AVCodecContext* ctx) const noexcept
×
392
  {
393
#if SCORE_HAS_LIBAV
394
    avcodec_free_context(&ctx);
×
395
#endif
396
  }
×
397
};
398
struct AVFormatContext_Free
399
{
400
  void operator()(AVFormatContext* ctx) const noexcept
×
401
  {
402
#if SCORE_HAS_LIBAV
403
    avformat_close_input(&ctx);
×
404
#endif
405
  }
×
406
};
407
struct AVFrame_Free
408
{
409
  void operator()(AVFrame* frame) const noexcept
×
410
  {
411
#if SCORE_HAS_LIBAV
412
    av_frame_free(&frame);
×
413
#endif
414
  }
×
415
};
416
using AVFormatContext_ptr = std::unique_ptr<AVFormatContext, AVFormatContext_Free>;
417
using AVCodecContext_ptr = std::unique_ptr<AVCodecContext, AVCodecContext_Free>;
418
using AVFrame_ptr = std::unique_ptr<AVFrame, AVFrame_Free>;
419

NEW
420
AVFormatContext_ptr open_audio(const QString& path, AvIoDevice& io)
×
421
{
422
#if SCORE_HAS_LIBAV
423
  AVFormatContext* fmt_ctx_ptr{};
×
NEW
424
  if(isStreamedMediaPath(path))
×
425
  {
NEW
426
    if(!open_input_custom_io(fmt_ctx_ptr, io, path))
×
NEW
427
      throw std::runtime_error(
×
NEW
428
          "Couldn't open file: " + path.toStdString() + " (custom IO)");
×
NEW
429
  }
×
430
  else
431
  {
NEW
432
    auto l1 = path.toUtf8();
×
NEW
433
    auto ret = avformat_open_input(&fmt_ctx_ptr, l1.constData(), nullptr, nullptr);
×
NEW
434
    if(ret != 0)
×
435
    {
NEW
436
      char err[100]{0};
×
NEW
437
      av_make_error_string(err, 100, ret);
×
NEW
438
      throw std::runtime_error(
×
NEW
439
          "Couldn't open file: " + std::string(l1.constData()) + " => "
×
NEW
440
          + std::string(err));
×
441
    }
UNCOV
442
  }
×
443

444
  AVDictionaryEntry* tag = NULL;
×
445
  while((tag = av_dict_get(fmt_ctx_ptr->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
×
446
  {
447
    using namespace std::literals;
448
    if(tag->key != "Comment"sv)
×
449
      qDebug() << tag->key << "=" << tag->value;
×
450
  }
451

452
  return AVFormatContext_ptr{fmt_ctx_ptr};
×
453
#else
454
  return {};
455
#endif
456
}
×
457

458
std::optional<AudioInfo> AudioDecoder::do_probe(const QString& path)
×
459
try
460
{
461
#if SCORE_HAS_LIBAV
NEW
462
  AvIoDevice io;
×
NEW
463
  auto fmt_ctx = open_audio(path, io);
×
464

465
  if(avformat_find_stream_info(fmt_ctx.get(), nullptr) < 0)
×
466
    return {};
×
467

468
  for(std::size_t i = 0; i < fmt_ctx->nb_streams; i++)
×
469
  {
470
    if(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
×
471
    {
472
      auto stream = fmt_ctx->streams[i];
×
473
      AudioInfo info;
×
474
      info.audioStream = i;
×
475
      info.channels = ossia::avstream_get_audio_channels(*stream);
×
476
      if(info.channels == 0)
×
477
        return {};
×
478
      info.fileRate = stream->codecpar->sample_rate;
×
479
      info.fileLength = std::ceil(info.fileRate * read_length(path));
×
480
      info.max_arr_length = info.fileLength;
×
481
      info.tempo = estimateTempo(path);
×
482

483
      /*
484
        if (info.rate != m_targetSampleRate)
485
        {
486
          info.length
487
              = av_rescale_rnd(info.length, m_targetSampleRate, info.rate,
488
        AV_ROUND_UP);
489

490
        if (info.length > info.max_arr_length)
491
          info.max_arr_length = info.length;
492
        }
493
      */
494

495
      return info;
×
496
    }
497
  }
×
498
#endif
499
  return {};
×
500
}
×
501
catch(...)
502
{
503
  return {};
×
504
}
×
505

506
QHash<QString, AudioInfo>& AudioDecoder::database()
×
507
{
508
  static QHash<QString, AudioInfo> db;
×
509
  return db;
×
510
}
511

512
auto debug_ffmpeg(int ret, QString ctx)
×
513
{
514
#if SCORE_HAS_LIBAV
515
  if(ret < 0 && ret != AVERROR_EOF)
×
516
  {
517
    char err[100]{0};
×
518
    av_make_error_string(err, 100, ret);
×
519
    qDebug() << "error when " << ctx << ": " << ret << err;
×
520
  }
×
521
  else
522
  {
523
    // qDebug()<<"ok: "<< ctx ;
524
  }
525
#endif
526
}
×
527

528
double AudioDecoder::read_length(const QString& path)
×
529
{
530
#if SCORE_HAS_LIBAV
531
  auto info = score::libav::probe_or_throw(path);
×
532

533
  AVDictionaryEntry* tag = nullptr;
×
534
  while(
×
535
      (tag = av_dict_get(info.format_context->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
×
536
  {
537
    using namespace std::literals;
538
    if(tag->key != "Comment"sv)
×
539
      qDebug() << tag->key << "=" << tag->value;
×
540
  }
541

542
  return info.duration_seconds().value_or(0.);
×
543
#else
544
  return 0;
545
#endif
546
}
×
547

548
void AudioDecoder::decode(const QString& path, int trackToUse, audio_handle hdl)
×
549
{
550
#if SCORE_HAS_LIBAV
551
  SCORE_ASSERT(hdl);
×
552
  AudioInfo info;
×
553
  auto it = database().find(path);
×
554
  if(it == database().end())
×
555
  {
556
    try
557
    {
558
      if(auto res = probe(path))
×
559
        info = *res;
×
560
    }
×
561
    catch(...)
562
    {
563
      qDebug("Cannot decode without info");
×
564
      finishedDecoding(hdl);
×
565
      return;
566
    }
×
567
  }
×
568
  else
569
  {
570
    info = *it;
×
571
  }
572

573
  track = trackToUse;
×
574
  decoded = 0;
×
575
  fileSampleRate = info.fileRate;
×
576
  channels = info.channels;
×
577
  auto& data = hdl->data;
×
578
  data.resize(info.channels);
×
579

580
  if(convertedSampleRate != fileSampleRate)
×
581
    info.max_arr_length = av_rescale_rnd(
×
582
        info.max_arr_length, convertedSampleRate, fileSampleRate, AV_ROUND_UP);
×
583

584
  for(auto& c : data)
×
585
  {
586
    c.reserve(info.max_arr_length * 1.1);
×
587
    c.resize(info.max_arr_length);
×
588
  }
589

590
  if(data.size() == 0)
×
591
    return;
×
592

593
  this->moveToThread(&m_decodeThread);
×
594
  m_decodeThread.start();
×
595
  startDecode(path, hdl);
×
596
#endif
597
}
×
598

599
std::optional<std::pair<AudioInfo, audio_array>>
600
AudioDecoder::decode_synchronous(const QString& path, int rate)
×
601
{
602
  auto res = probe(path);
×
603
  if(!res)
×
604
    return std::nullopt;
×
605

606
  AudioDecoder dec(rate);
×
607

608
  dec.fileSampleRate = res->fileRate;
×
609
  dec.channels = res->channels;
×
610

611
  auto hdl = std::make_shared<ossia::audio_data>();
×
612
  hdl->data.resize(res->channels);
×
613
  for(auto& c : hdl->data)
×
614
  {
615
    c.reserve(res->max_arr_length * 1.1);
×
616
    c.resize(res->max_arr_length);
×
617
  }
618

619
  dec.on_startDecode(path, hdl);
×
620

621
  return std::make_pair(*std::move(res), std::move(hdl->data));
×
622
}
×
623

624
template <typename Decoder>
625
void AudioDecoder::decodeFrame(Decoder dec, audio_array& data, AVFrame& frame)
×
626
{
627
#if SCORE_HAS_LIBAV
628
  const std::size_t channels = data.size();
×
629
  const std::size_t max_samples = data[0].size();
×
630

631
  if(convertedSampleRate != fileSampleRate)
×
632
  {
633
    auto new_len = av_rescale_rnd(
×
634
        frame.nb_samples, convertedSampleRate, fileSampleRate, AV_ROUND_UP);
×
635

636
    if(decoded + new_len > max_samples)
×
637
    {
638
      qDebug() << "ERROR" << decoded + frame.nb_samples << ">" << max_samples;
×
639
      return;
×
640
    }
641

642
    audio_array tmp;
×
643
    tmp.resize(channels);
×
644
    for(auto& sub : tmp)
×
645
      sub.resize(frame.nb_samples * 2);
×
646

647
    dec(tmp, 0, frame.extended_data, frame.nb_samples);
×
648

649
    int res = 0;
×
650
    for(std::size_t i = 0; i < channels; ++i)
×
651
    {
652
      audio_sample* out_ptr = data[i].data() + decoded;
×
653
      audio_sample* in_ptr = tmp[i].data();
×
654

655
      res = swr_convert(
×
656
          resampler[i], (uint8_t**)&out_ptr, new_len, (const uint8_t**)&in_ptr,
×
657
          frame.nb_samples);
×
658
    }
×
659
    decoded += res;
×
660
  }
×
661
  else
662
  {
663
    if(decoded + frame.nb_samples > max_samples)
×
664
    {
665
      qDebug() << "ERROR" << decoded + frame.nb_samples << ">" << max_samples;
×
666
      return;
×
667
    }
668

669
    dec(data, decoded, frame.extended_data, frame.nb_samples);
×
670
    decoded += frame.nb_samples;
×
671
  }
672
#endif
673
}
×
674

675
template <typename Decoder>
676
void AudioDecoder::decodeRemaining(Decoder dec, audio_array& data, AVFrame& frame)
×
677
{
678
#if SCORE_HAS_LIBAV
679
  const std::size_t channels = data.size();
×
680
  if(convertedSampleRate != fileSampleRate)
×
681
  {
682
    int res = 0;
×
683
    for(std::size_t i = 0; i < channels; ++i)
×
684
    {
685
      audio_sample* out_ptr = data[i].data() + decoded;
×
686

687
      res = swr_convert(
×
688
          resampler[i], (uint8_t**)&out_ptr, data[i].size() - decoded, nullptr, 0);
×
689
    }
×
690
    decoded += res;
×
691
  }
×
692
  else
693
  {
694
    /*
695
        const std::size_t channels = data.size();
696
        const std::size_t max_samples = data[0].size();
697
        if (decoded + frame.nb_samples > max_samples)
698
        {
699
          qDebug() << "ERROR" << decoded + frame.nb_samples << ">" <<
700
       max_samples; return;
701
        }
702

703
        dec(data, decoded, frame.extended_data, frame.nb_samples);
704
        decoded += frame.nb_samples;
705
        */
706
  }
707

708
  // TODO it should be zeros, but check to be sure..
709
  if(decoded < data[0].size())
×
710
    decoded = data[0].size();
×
711
#endif
712
}
×
713
void AudioDecoder::on_startDecode(QString path, audio_handle hdl)
×
714
{
715
#if SCORE_HAS_LIBAV
716
  auto& data = hdl->data;
×
717
  try
718
  {
719
    const std::size_t channels = data.size();
×
720

NEW
721
    AvIoDevice io;
×
NEW
722
    auto fmt_ctx = open_audio(path, io);
×
723

724
    auto ret = avformat_find_stream_info(fmt_ctx.get(), nullptr);
×
725
    if(ret != 0)
×
726
      throw std::runtime_error("Couldn't find stream information");
×
727

728
    AVStream* stream{};
×
729
    if(track >= 0 && track < (int)fmt_ctx->nb_streams)
×
730
    {
731
      stream = fmt_ctx->streams[track];
×
732
      for(std::size_t i = 0; i < fmt_ctx->nb_streams; i++)
×
733
      {
734
        if(int(i) != track)
×
735
        {
736
          fmt_ctx->streams[i]->discard = AVDISCARD_ALL;
×
737
        }
×
738
      }
×
739
    }
×
740
    else
741
    {
742
      // Find the first audio stream
743
      for(std::size_t i = 0; i < fmt_ctx->nb_streams; i++)
×
744
      {
745
        if(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && !stream)
×
746
        {
747
          stream = fmt_ctx->streams[i];
×
748
        }
×
749
        else
750
        {
751
          fmt_ctx->streams[i]->discard = AVDISCARD_ALL;
×
752
        }
753
      }
×
754
    }
755

756
    if(!stream)
×
757
      throw std::runtime_error("Couldn't find any audio stream");
×
758

759
    auto codec = avcodec_find_decoder(stream->codecpar->codec_id);
×
760
    if(!codec)
×
761
      throw std::runtime_error("Couldn't find codec");
×
762

763
    AVCodecContext_ptr codec_ctx{avcodec_alloc_context3(codec)};
×
764
    if(!codec_ctx)
×
765
      throw std::runtime_error("Couldn't allocate codec context");
×
766

767
    ret = avcodec_parameters_to_context(codec_ctx.get(), stream->codecpar);
×
768
    if(ret != 0)
×
769
      throw std::runtime_error("Couldn't copy codec data");
×
770

771
    ret = avcodec_open2(codec_ctx.get(), codec, nullptr);
×
772
    if(ret != 0)
×
773
      throw std::runtime_error("Couldn't open codec");
×
774

775
    auto decoder = make_decoder(*stream);
×
776

777
    // init resampling
778
    if(convertedSampleRate != fileSampleRate)
×
779
    {
780
      for(std::size_t i = 0; i < channels; ++i)
×
781
      {
782
#if LIBSWRESAMPLE_VERSION_MAJOR <= 4
783
        SwrContext* swr = swr_alloc_set_opts(
×
784
            nullptr, AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_FLT, convertedSampleRate,
×
785
            AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_FLT, fileSampleRate, 0, nullptr);
×
786
#else
787
        SwrContext* swr = nullptr;
788
        static constexpr AVChannelLayout mono_layout AV_CHANNEL_LAYOUT_MONO;
789
        int ret = swr_alloc_set_opts2(
790
            &swr, &mono_layout, AV_SAMPLE_FMT_FLT, convertedSampleRate, &mono_layout,
791
            AV_SAMPLE_FMT_FLT, fileSampleRate, 0, nullptr);
792

793
        if(ret != 0)
794
          throw std::runtime_error("Couldn't open resampler");
795
#endif
796
        swr_init(swr);
×
797
        resampler.push_back(swr);
×
798
      }
×
799
    }
×
800

801
    auto read_frame = [&](AVPacket& packet) {
×
802
      int ret{};
×
803
      ret = av_read_frame(fmt_ctx.get(), &packet);
×
804

805
      while(ret >= 0 && ret != AVERROR(EOF) && packet.stream_index != stream->index)
×
806
      {
807
        av_packet_unref(&packet);
×
808
        ret = av_read_frame(fmt_ctx.get(), &packet);
×
809
      }
810

811
      return ret;
×
812
    };
813
    // decoding
814
    ossia::visit(
×
815
        [&](auto& dec) {
×
816
      AVPacket packet;
817
      AVFrame_ptr frame{av_frame_alloc()};
×
818

819
      ret = read_frame(packet);
×
820

821
      debug_ffmpeg(ret, "av_read_frame");
×
822
      int update = 0;
×
823
      while(ret >= 0)
×
824
      {
825
        ret = avcodec_send_packet(codec_ctx.get(), &packet);
×
826
        debug_ffmpeg(ret, "avcodec_send_packet");
×
827
        if(ret == 0)
×
828
        {
829
          ret = avcodec_receive_frame(codec_ctx.get(), frame.get());
×
830
          debug_ffmpeg(ret, "avcodec_receive_frame");
×
831
          if(ret == 0)
×
832
          {
833
            while(ret == 0)
×
834
            {
835
              decodeFrame(dec, data, *frame);
×
836
              ret = avcodec_receive_frame(codec_ctx.get(), frame.get());
×
837

838
              update++;
×
839
              if((update % 512) == 0)
×
840
              {
841
                newData();
×
842
              }
×
843
            }
844

845
            av_packet_unref(&packet);
×
846
            ret = read_frame(packet);
×
847
            debug_ffmpeg(ret, "av_read_frame");
×
848
            continue;
×
849
          }
850
          else if(ret == AVERROR(EAGAIN))
×
851
          {
852
            av_packet_unref(&packet);
×
853
            ret = read_frame(packet);
×
854
            debug_ffmpeg(ret, "av_read_frame");
×
855
            continue;
×
856
          }
857
          else if(ret == AVERROR_EOF)
×
858
          {
859
            decodeFrame(dec, data, *frame);
×
860
            break;
×
861
          }
862
          else
863
          {
864
            break;
×
865
          }
866
        }
867
        else if(ret == AVERROR(EAGAIN))
×
868
        {
869
          ret = avcodec_receive_frame(codec_ctx.get(), frame.get());
×
870
          debug_ffmpeg(ret, "avcodec_receive_frame EAGAIN");
×
871
        }
×
872
        else
873
        {
874
          break;
×
875
        }
876
      }
877

878
      // Flush
879
      ret = avcodec_send_packet(codec_ctx.get(), nullptr);
×
880

881
      decodeRemaining(dec, data, *frame);
×
882
      newData();
×
883
        },
×
884
        decoder);
885

886
    // clear resampling
887
    for(auto swr : resampler)
×
888
      swr_free(&swr);
×
889
    resampler.clear();
×
890
  }
×
891
  catch(std::exception& e)
892
  {
893
    qDebug() << "Decoder error: " << e.what();
×
894
  }
×
895

896
  finishedDecoding(hdl);
×
897
  m_decodeThread.quit();
×
898

899
#endif
900
  return;
×
901
}
×
902

903
TimeVal AudioInfo::duration() const noexcept
×
904
{
905
  if(fileRate == 0 || fileLength == 0)
×
906
    return TimeVal::zero();
×
907

908
  if(tempo)
×
909
  {
910
    // These aren't "real" milliseconds, but the "model" / "UI" milliseconds that
911
    // are tempo-invariant, e.g. 2000 msec = 1 4/4 bar
912
    double model_msecs
×
913
        = 1000. * (double(fileLength) / double(fileRate)) * (*tempo / ossia::root_tempo);
×
914

915
    model_msecs = std::round(model_msecs / 100.) * 100.;
×
916
    TimeVal msecs = TimeVal::fromMsecs(model_msecs);
×
917
    return msecs;
×
918
  }
919
  else
920
  {
921
    return TimeVal::fromMsecs(1000. * (double(fileLength) / double(fileRate)));
×
922
  }
923
}
×
924

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