kwave  18.07.70
Kwave::OpusDecoder Class Reference

#include <OpusDecoder.h>

Inheritance diagram for Kwave::OpusDecoder:
Inheritance graph
Collaboration diagram for Kwave::OpusDecoder:
Collaboration graph

Public Member Functions

 OpusDecoder (QIODevice *source, ogg_sync_state &oy, ogg_stream_state &os, ogg_page &og, ogg_packet &op)
 
virtual ~OpusDecoder () Q_DECL_OVERRIDE
 
virtual int open (QWidget *widget, Kwave::FileInfo &info) Q_DECL_OVERRIDE
 
virtual int decode (Kwave::MultiWriter &dst) Q_DECL_OVERRIDE
 
virtual void reset () Q_DECL_OVERRIDE
 
virtual void close (Kwave::FileInfo &info) Q_DECL_OVERRIDE
 
- Public Member Functions inherited from Kwave::OggSubDecoder
virtual ~OggSubDecoder ()
 

Protected Member Functions

void parseComment (Kwave::FileInfo &info, const QString &comment)
 
virtual int parseOpusHead (QWidget *widget, Kwave::FileInfo &info)
 
virtual int parseOpusTags (QWidget *widget, Kwave::FileInfo &info)
 

Private Attributes

QIODevice * m_source
 
qint64 m_stream_start_pos
 
sample_index_t m_samples_written
 
ogg_sync_state & m_oy
 
ogg_stream_state & m_os
 
ogg_page & m_og
 
ogg_packet & m_op
 
Kwave::opus_header_t m_opus_header
 
OpusMSDecoder * m_opus_decoder
 
Kwave::VorbisCommentMap m_comments_map
 
float * m_raw_buffer
 
Kwave::MultiTrackSink< Kwave::SampleBuffer, true > * m_buffer
 
Kwave::StreamObjectm_rate_converter
 
bool m_output_is_connected
 
unsigned int m_packet_count
 
quint64 m_samples_raw
 
quint64 m_bytes_count
 
int m_packet_len_min
 
int m_packet_len_max
 
int m_packet_size_min
 
int m_packet_size_max
 
qint64 m_granule_first
 
qint64 m_granule_last
 
qint64 m_granule_offset
 
int m_preskip
 

Detailed Description

Definition at line 45 of file OpusDecoder.h.

Constructor & Destructor Documentation

◆ OpusDecoder()

Kwave::OpusDecoder::OpusDecoder ( QIODevice *  source,
ogg_sync_state &  oy,
ogg_stream_state &  os,
ogg_page &  og,
ogg_packet &  op 
)

Constructor

Parameters
sourcepointer to a QIODevice to read from, must not be null
oysync and verify incoming physical bitstream
ostake physical pages, weld into a logical stream of packets
ogone Ogg bitstream page, Opus packets are inside
opone raw packet of data for decode

Definition at line 55 of file OpusDecoder.cpp.

61  m_oy(oy), m_os(os), m_og(og), m_op(op), m_opus_decoder(Q_NULLPTR),
62  m_comments_map(), m_raw_buffer(Q_NULLPTR), m_buffer(Q_NULLPTR),
63  m_rate_converter(Q_NULLPTR),
64  m_output_is_connected(false),
69  m_preskip(0)
70 {
71 }
Kwave::StreamObject * m_rate_converter
Definition: OpusDecoder.h:155
qint64 m_stream_start_pos
Definition: OpusDecoder.h:122
unsigned int m_packet_count
Definition: OpusDecoder.h:164
QIODevice * m_source
Definition: OpusDecoder.h:119
OpusMSDecoder * m_opus_decoder
Definition: OpusDecoder.h:143
Kwave::VorbisCommentMap m_comments_map
Definition: OpusDecoder.h:146
Kwave::MultiTrackSink< Kwave::SampleBuffer, true > * m_buffer
Definition: OpusDecoder.h:152
sample_index_t m_samples_written
Definition: OpusDecoder.h:125
ogg_sync_state & m_oy
Definition: OpusDecoder.h:128
ogg_stream_state & m_os
Definition: OpusDecoder.h:131
ogg_packet & m_op
Definition: OpusDecoder.h:137

◆ ~OpusDecoder()

virtual Kwave::OpusDecoder::~OpusDecoder ( )
inlinevirtual

destructor

Definition at line 64 of file OpusDecoder.h.

References close(), decode(), open(), parseComment(), parseOpusHead(), parseOpusTags(), and reset().

64 {}
Here is the call graph for this function:

Member Function Documentation

◆ close()

void Kwave::OpusDecoder::close ( Kwave::FileInfo info)
virtual

finish the decoding, last chance to fix up some file info

Parameters
inforeference to a FileInfo to fill

Implements Kwave::OggSubDecoder.

Definition at line 644 of file OpusDecoder.cpp.

References Kwave::MultiTrackSink< SINK, INITIALIZE >::at(), Kwave::BITRATE_MODE_CBR_HARD, Kwave::BITRATE_MODE_VBR, Kwave::SampleBuffer::finished(), Kwave::INF_BITRATE_MODE, Kwave::INF_BITRATE_NOMINAL, Kwave::INF_OPUS_FRAME_LEN, m_buffer, m_bytes_count, m_opus_header, m_output_is_connected, m_packet_count, m_packet_len_max, m_packet_len_min, m_packet_size_max, m_packet_size_min, m_rate_converter, m_samples_raw, m_samples_written, Kwave::opus_next_sample_rate(), reset(), Kwave::FileInfo::set(), and Kwave::toInt().

Referenced by ~OpusDecoder().

645 {
646  // flush the buffer of the sample rate converter, to avoid missing
647  // samples due to the limitations of libsamplerate
648  if (m_buffer) {
649  const unsigned int tracks = m_opus_header.channels;
650  for (unsigned int t = 0; t < tracks; t++) {
651  Kwave::SampleBuffer *buffer = m_buffer->at(t);
652  Q_ASSERT(buffer);
653  buffer->finished();
654  }
655  }
656 
657  if (m_buffer) delete m_buffer;
658  m_buffer = Q_NULLPTR;
659 
660  delete m_rate_converter;
661  m_rate_converter = Q_NULLPTR;
662 
663  m_output_is_connected = false;
664 
665  qDebug(" OpusDecoder: packet count=%u", m_packet_count);
666  qDebug(" OpusDecoder: packet length: %d...%d samples",
668  qDebug(" OpusDecoder: packet size: %d...%d bytes",
670 
673  {
674  // detected hard CBR mode
676  qDebug(" OpusDecoder: hard CBR mode");
677  } else {
678  // otherwise default to VBR mode
680  qDebug(" OpusDecoder: VBR mode");
681  }
682 
683  // determine the avarage frame length in ms
684  qreal avg_ms = (static_cast<qreal>(m_samples_raw) /
685  static_cast<qreal>(m_packet_count)) / 48.0;
686  qDebug(" OpusDecoder: average frame length: %0.1f ms", avg_ms);
687  info.set(INF_OPUS_FRAME_LEN, QVariant(avg_ms));
688 
689  // calculate the bitrate == n_bits / n_seconds
690  // n_bits = n_bytes * 8
691  // n_seconds = n_samples / sample_rate
692  // => bitrate = (n_bytes * 8) / (n_samples / sample_rate)
693  const double sr = Kwave::opus_next_sample_rate(m_opus_header.sample_rate);
694  int bitrate = Kwave::toInt(
695  ((m_bytes_count * 8) * sr) / m_samples_written);
696  qDebug(" OpusDecoder: average bitrate: %d bits/sec", bitrate);
697  info.set(INF_BITRATE_NOMINAL, QVariant(bitrate));
698 
699  reset();
700 }
int opus_next_sample_rate(int rate)
Definition: OpusCommon.cpp:30
Kwave::StreamObject * m_rate_converter
Definition: OpusDecoder.h:155
unsigned int m_packet_count
Definition: OpusDecoder.h:164
Kwave::opus_header_t m_opus_header
Definition: OpusDecoder.h:140
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
int toInt(T x)
Definition: Utils.h:127
Kwave::MultiTrackSink< Kwave::SampleBuffer, true > * m_buffer
Definition: OpusDecoder.h:152
virtual void finished()
sample_index_t m_samples_written
Definition: OpusDecoder.h:125
virtual SINK * at(unsigned int track) const
virtual void reset() Q_DECL_OVERRIDE
Here is the call graph for this function:
Here is the caller graph for this function:

◆ decode()

int Kwave::OpusDecoder::decode ( Kwave::MultiWriter dst)
virtual

decode received ogg data

Parameters
dsta MultiWriter to be used as sink
Returns
-1 if failed or >= 0 if succeeded

Implements Kwave::OggSubDecoder.

Definition at line 503 of file OpusDecoder.cpp.

References Kwave::MultiTrackSink< SINK, INITIALIZE >::at(), Kwave::connect(), DBG, double2sample(), m_buffer, m_bytes_count, m_granule_first, m_granule_last, m_granule_offset, m_og, m_op, m_opus_decoder, m_opus_header, m_output_is_connected, m_packet_count, m_packet_len_max, m_packet_len_min, m_packet_size_max, m_packet_size_min, m_preskip, m_rate_converter, m_raw_buffer, m_samples_raw, m_samples_written, MAX_FRAME_SIZE, Kwave::opus_error(), Kwave::SampleBuffer::put(), SAMPLE_MAX, SAMPLE_MIN, and Kwave::toInt().

Referenced by ~OpusDecoder().

504 {
505  if (!m_opus_decoder || !m_raw_buffer || !m_buffer)
506  return -1;
507 
508  // get some statistical data, for bitrate mode detection
509  m_packet_count++;
510 
511  int frames = opus_packet_get_nb_frames(
512  m_op.packet,
513  static_cast<opus_int32>(m_op.bytes)
514  );
515  if(frames < 1 || frames > 48) {
516  qWarning("WARNING: Invalid packet TOC in packet #%llu",
517  static_cast<unsigned long long int>(m_op.packetno));
518  }
519  int spf = opus_packet_get_samples_per_frame(m_op.packet, 48000);
520  int spp = frames * spf;
521  if (spp < 120 || spp > 5760 || (spp % 120) != 0) {
522  qWarning("WARNING: Invalid packet TOC in packet #%llu",
523  static_cast<unsigned long long int>(m_op.packetno));
524  }
525 
526  if (spp < m_packet_len_min) m_packet_len_min = spp;
527  if (spp > m_packet_len_max) m_packet_len_max = spp;
528  if (m_op.bytes < m_packet_size_min)
530  if (m_op.bytes > m_packet_size_max)
532 
533  // total count of samples and bytes
534  m_samples_raw += spp;
535  m_bytes_count += m_op.bytes;
536 
537  // granule pos handling
538  const qint64 gp = static_cast<qint64>(ogg_page_granulepos(&m_og));
539  if (gp >= 0) {
540  if (gp < m_granule_first) m_granule_first = gp;
541  if (gp > m_granule_last) m_granule_last = gp;
543  // calculate how many samples might be missing at the start
545  }
546  }
547 
548  // decode the audio data into a buffer with float values
549  int length = opus_multistream_decode_float(
551  static_cast<const unsigned char *>(m_op.packet),
552  static_cast<opus_int32>(m_op.bytes),
554  );
555  if (length <= 0) {
556  qWarning("OpusDecoder::decode() failed: '%s'",
557  DBG(Kwave::opus_error(length)));
558  return -1;
559  }
560  Q_ASSERT(length <= MAX_FRAME_SIZE);
561 
562  // manually apply the gain if necessary
563  if (m_opus_header.gain) {
564  const float g = powf(10.0f, m_opus_header.gain / (20.0f * 256.0f));
565  for (int i = 0; i < (length * m_opus_header.channels); i++)
566  m_raw_buffer[i] *= g;
567  }
568 
569  const unsigned int tracks = m_opus_header.channels;
570 
571  if (!m_output_is_connected) {
572  Kwave::StreamObject *src =
574  if (!Kwave::connect(*src, SIGNAL(output(Kwave::SampleArray)),
575  dst, SLOT(input(Kwave::SampleArray))) )
576  {
577  qWarning("OpusDecoder::decode() connecting output failed");
578  return -1;
579  }
580  m_output_is_connected = true;
581  }
582 
583  // handle preskip
584  const float *p = m_raw_buffer;
585  if (m_preskip) {
586  if (m_preskip >= length) {
587  m_preskip -= length;
588  return 0; // skip the complete buffer
589  }
590  // shrink buffer by preskip samples
591  length -= m_preskip;
592  p += m_preskip * tracks;
593  m_preskip = 0;
594  }
595 
596  // check trailing data at the end (after the granule)
598  m_opus_header.preskip;
599 
600  if ((m_samples_written + length) > last) {
601  int diff = Kwave::toInt((m_samples_written + length) - last);
602  if (diff > length) return 0;
603  length -= diff;
604  }
605 
606  // convert the buffer from float to sample_t, blockwise...
607  for (unsigned int t = 0; t < tracks; t++) {
608  Kwave::SampleBuffer *buffer = m_buffer->at(t);
609  const float *in = p + t;
610  for (int frame = 0; frame < length; frame++) {
611  // scale, use some primitive noise shaping + clipping
612  double noise = (drand48() - double(0.5)) / double(SAMPLE_MAX);
613  double d = static_cast<double>(*in);
614  sample_t s = qBound<sample_t>(
615  SAMPLE_MIN, double2sample(d + noise), SAMPLE_MAX
616  );
617  buffer->put(s);
618  in += tracks;
619  }
620  }
621 
622  m_samples_written += length;
623 
624  // update the progress bar
625  QApplication::processEvents();
626 
627  return 0;
628 }
Kwave::StreamObject * m_rate_converter
Definition: OpusDecoder.h:155
unsigned int m_packet_count
Definition: OpusDecoder.h:164
#define SAMPLE_MIN
Definition: Sample.h:49
Kwave::opus_header_t m_opus_header
Definition: OpusDecoder.h:140
OpusMSDecoder * m_opus_decoder
Definition: OpusDecoder.h:143
quint64 sample_index_t
Definition: Sample.h:28
void put(sample_t sample)
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
#define MAX_FRAME_SIZE
Definition: OpusDecoder.cpp:52
int toInt(T x)
Definition: Utils.h:127
Kwave::MultiTrackSink< Kwave::SampleBuffer, true > * m_buffer
Definition: OpusDecoder.h:152
QString opus_error(int err)
Definition: OpusCommon.cpp:45
static sample_t double2sample(const double f)
Definition: Sample.h:81
#define SAMPLE_MAX
Definition: Sample.h:52
sample_index_t m_samples_written
Definition: OpusDecoder.h:125
#define DBG(qs)
Definition: String.h:55
virtual SINK * at(unsigned int track) const
qint32 sample_t
Definition: Sample.h:37
ogg_packet & m_op
Definition: OpusDecoder.h:137
Here is the call graph for this function:
Here is the caller graph for this function:

◆ open()

int Kwave::OpusDecoder::open ( QWidget *  widget,
Kwave::FileInfo info 
)
virtual

parse the header of the stream and initialize the decoder

Parameters
widgeta QWidget to be used as parent for error messages
inforeference to a FileInfo to fill
Returns
-1 if failed or +1 if succeeded

Implements Kwave::OggSubDecoder.

Definition at line 373 of file OpusDecoder.cpp.

References Kwave::connect(), Kwave::FileInfo::dump(), Kwave::MessageBox::error(), Kwave::INF_ESTIMATED_LENGTH, m_buffer, m_bytes_count, m_granule_first, m_granule_last, m_granule_offset, m_opus_decoder, m_opus_header, m_packet_count, m_packet_len_max, m_packet_len_min, m_packet_size_max, m_packet_size_min, m_preskip, m_rate_converter, m_raw_buffer, m_samples_raw, m_samples_written, m_source, m_stream_start_pos, MAX_FRAME_SIZE, Kwave::opus_error(), Kwave::opus_next_sample_rate(), parseOpusHead(), parseOpusTags(), Kwave::FileInfo::set(), and Kwave::StreamObject::setAttribute().

Referenced by ~OpusDecoder().

374 {
375  // extract the initial header from the first page and verify that the
376  // Ogg bitstream is in fact valid/usable Opus data
377  if (parseOpusHead(widget, info) < 1)
378  return -1;
379 
380  // extract the second packet, it should contain the comments...
381  if (parseOpusTags(widget, info) < 1)
382  return -1;
383 
384  // allocate memory for the output data
385  if (m_raw_buffer) free(m_raw_buffer);
386  m_raw_buffer = static_cast<float *>(
387  malloc(sizeof(float) * MAX_FRAME_SIZE * m_opus_header.channels));
388  if (!m_raw_buffer) {
389  Kwave::MessageBox::error(widget, i18n("Out of memory"));
390  return -1;
391  }
392 
393  int err = OPUS_BAD_ARG;
394  qDebug(" sample rate = %d", m_opus_header.sample_rate);
395  m_opus_decoder = opus_multistream_decoder_create(
397  m_opus_header.channels,
398  m_opus_header.streams,
399  m_opus_header.coupled,
400  m_opus_header.map,
401  &err
402  );
403 
404  if ((err != OPUS_OK) || !m_opus_decoder) {
405  info.dump();
407  i18n("Opus decoder failed"));
408  return -1;
409  }
410 
411 #ifdef OPUS_SET_GAIN
412  if (m_opus_header.gain) {
413  err = opus_multistream_decoder_ctl(
414  m_opus_decoder, OPUS_SET_GAIN(m_opus_header.gain)
415  );
416  if (err == OPUS_OK) {
417  qDebug(" OpusDecoder: gain adjusted to %d Q8dB",
418  m_opus_header.gain);
419  m_opus_header.gain = 0;
420  }
421  }
422 #endif /* OPUS_SET_GAIN */
423 
424  const unsigned int tracks = m_opus_header.channels;
425  int rate_orig = m_opus_header.sample_rate;
426  int rate_supp = Kwave::opus_next_sample_rate(rate_orig);
427 
428  // create a multi track sample buffer
430  Q_ASSERT(m_buffer);
431  if (!m_buffer) return -1;
432 
433  // handle sample rate conversion
434  if (rate_orig != rate_supp) {
435  bool ok = true;
436 
437  qDebug(" OpusDecoder::open(): converting sample rate: %d -> %d",
438  rate_supp, rate_orig);
439 
442  Q_ASSERT(m_rate_converter);
443  if (!m_rate_converter) ok = false;
444 
445  if (ok) {
446  double rate_from = static_cast<double>(rate_supp);
447  double rate_to = static_cast<double>(rate_orig);
449  SLOT(setRatio(QVariant)),
450  QVariant(rate_to / rate_from)
451  );
452 
453  ok = Kwave::connect(
454  *m_buffer, SIGNAL(output(Kwave::SampleArray)),
455  *m_rate_converter, SLOT(input(Kwave::SampleArray))
456  );
457  }
458 
459  if (!ok) {
460  qWarning("OpusDecoder::open(): creating rate converter failed!");
461  }
462 
463  if (!ok) {
464  qDebug("OpusDecoder::open(): sample rate %d is not "
465  "supported but rate conversion is not available "
466  "-> setting to %d", rate_orig, rate_supp);
467  m_opus_header.sample_rate = rate_supp;
468  }
469  }
470 
471  // estimate the length of the file from file size, bitrate, channels
472  if (!m_source->isSequential()) {
473  qint64 file_size = m_source->size();
474  qreal bitrate = 196000; // just guessed
475  qreal rate = rate_orig;
476  qreal seconds = file_size / (bitrate / 8);
477  sample_index_t samples = static_cast<sample_index_t>(seconds * rate);
478 
479  qDebug(" OpusDecoder: estimated length: %llu samples", samples);
480  info.set(Kwave::INF_ESTIMATED_LENGTH, samples);
481  }
482 
483  m_stream_start_pos = m_source->pos();
484  m_samples_written = 0;
485  m_packet_count = 0;
486  m_samples_raw = 0;
487  m_bytes_count = 0;
488 
489  m_packet_len_min = std::numeric_limits<int>::max();
490  m_packet_len_max = 0;
491  m_packet_size_min = std::numeric_limits<int>::max();
492  m_packet_size_max = 0;
493 
494  m_preskip = m_opus_header.preskip;
495  m_granule_first = std::numeric_limits<qint64>::max();
496  m_granule_last = 0;
497  m_granule_offset = 0;
498 
499  return 1;
500 }
int opus_next_sample_rate(int rate)
Definition: OpusCommon.cpp:30
Kwave::StreamObject * m_rate_converter
Definition: OpusDecoder.h:155
qint64 m_stream_start_pos
Definition: OpusDecoder.h:122
unsigned int m_packet_count
Definition: OpusDecoder.h:164
QIODevice * m_source
Definition: OpusDecoder.h:119
Kwave::opus_header_t m_opus_header
Definition: OpusDecoder.h:140
OpusMSDecoder * m_opus_decoder
Definition: OpusDecoder.h:143
virtual int parseOpusTags(QWidget *widget, Kwave::FileInfo &info)
quint64 sample_index_t
Definition: Sample.h:28
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
#define MAX_FRAME_SIZE
Definition: OpusDecoder.cpp:52
Kwave::MultiTrackSink< Kwave::SampleBuffer, true > * m_buffer
Definition: OpusDecoder.h:152
QString opus_error(int err)
Definition: OpusCommon.cpp:45
sample_index_t m_samples_written
Definition: OpusDecoder.h:125
virtual int parseOpusHead(QWidget *widget, Kwave::FileInfo &info)
void setAttribute(const char *attribute, const QVariant &value)
virtual void dump() const Q_DECL_OVERRIDE
Definition: FileInfo.cpp:460
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parseComment()

void Kwave::OpusDecoder::parseComment ( Kwave::FileInfo info,
const QString &  comment 
)
protected

Parses an Ogg comment into a into Kwave FileInfo. If more than one occurrence is found, they are concatenated as a semicolon separated list.

Parameters
infothe file info object to add the value
commentstring with the full comment, assumed "tag=value"

Definition at line 74 of file OpusDecoder.cpp.

References _, Kwave::FileInfo::contains(), DBG, Kwave::FileInfo::get(), m_comments_map, m_opus_header, Kwave::FileInfo::set(), and Kwave::toInt().

Referenced by parseOpusTags(), and ~OpusDecoder().

76 {
77  // assume that there is a '=' between tag and value
78  int pos = comment.indexOf(_("="));
79  if (pos < 1) {
80  qWarning("OpusDecoder: malformed comment: '%s'", DBG(comment));
81  return;
82  }
83 
84  // split into tag and value
85  QString tag = comment.left(pos).toUpper();
86  QString val = comment.mid(pos + 1).trimmed();
87 
88  // special handling for gain
89  if ((tag == _("REPLAY_TRACK_GAIN")) || (tag == _("REPLAY_ALBUM_GAIN"))) {
90  // gain in dB:
91  // remove "dB" from the end
92  val = val.left(val.indexOf(_("dB"), 0, Qt::CaseInsensitive)).trimmed();
93 
94  // convert to a integer Q8 dB value
95  bool ok = false;
96  int q8gain = Kwave::toInt(rint(val.toDouble(&ok) * 256.0));
97  if (ok && q8gain) {
98  m_opus_header.gain += q8gain;
99  qDebug(" OpusDecoder: %s %+0.1g dB", DBG(tag),
100  static_cast<double>(q8gain) / 256.0);
101  return;
102  }
103  } else if ((tag == _("R128_TRACK_GAIN")) || (tag == _("R128_ALBUM_GAIN"))) {
104  // R128_... already is a 7.8 integer value
105  bool ok = false;
106  int q8gain = Kwave::toInt(rint(val.toDouble(&ok)));
107  if (ok && q8gain) {
108  m_opus_header.gain += q8gain;
109  qDebug(" OpusDecoder: %s %+0.1g dB", DBG(tag),
110  static_cast<double>(q8gain) / 256.0);
111  return;
112  }
113  }
114 
115  // check for unknown properties
116  if (!m_comments_map.contains(tag)) {
117  qDebug("unsupported tag '%s', value='%s'", DBG(tag), DBG(val));
118  return;
119  }
120 
121  // set the new value
122  Kwave::FileProperty property = m_comments_map[tag];
123  if (info.contains(property)) {
124  // property already exists, append it
125  val = info.get(property).toString() + _("; ") + val;
126  }
127 
128  info.set(property, val);
129 }
bool contains(const FileProperty property) const
Definition: FileInfo.cpp:354
QVariant get(FileProperty key) const
Definition: FileInfo.cpp:372
Kwave::opus_header_t m_opus_header
Definition: OpusDecoder.h:140
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
Kwave::VorbisCommentMap m_comments_map
Definition: OpusDecoder.h:146
int toInt(T x)
Definition: Utils.h:127
#define _(m)
Definition: memcpy.c:66
#define DBG(qs)
Definition: String.h:55
FileProperty
Definition: FileInfo.h:45
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parseOpusHead()

int Kwave::OpusDecoder::parseOpusHead ( QWidget *  widget,
Kwave::FileInfo info 
)
protectedvirtual

parse the "OpusHeader" header of the stream

Parameters
widgeta QWidget to be used as parent for error messages
inforeference to a FileInfo to fill
Returns
-1 if failed or +1 if succeeded

Definition at line 245 of file OpusDecoder.cpp.

References Kwave::MessageBox::error(), Kwave::INF_COMPRESSION, m_op, m_opus_header, Kwave::Compression::OGG_OPUS, Kwave::FileInfo::set(), Kwave::FileInfo::setRate(), and Kwave::FileInfo::setTracks().

Referenced by open(), and ~OpusDecoder().

246 {
247  memset(&m_opus_header, 0x00, sizeof(m_opus_header));
248  memset(&m_opus_header.map, 0xFF, sizeof(m_opus_header.map));
249 
250  bool header_ok = false;
251  do {
252  if (!m_op.b_o_s || (m_op.bytes < 19)) {
253  qWarning("OpusDecoder::parseHeader(): header too short");
254  break;
255  }
256 
257  Kwave::opus_header_t *h =
258  reinterpret_cast<Kwave::opus_header_t *>(m_op.packet);
259 
260  // magic value
261  memcpy(&(m_opus_header.magic[0]), &(h->magic[0]),
262  sizeof(m_opus_header.magic));
263  if (memcmp(&(m_opus_header.magic[0]), "OpusHead", 8) != 0) {
264  qWarning("OpusDecoder::parseHeader(): OpusHead magic not found");
265  break; // this is no Opus stream ?
266  }
267 
268  // version number, only major version 0 is supported
269  m_opus_header.version = h->version;
270  if ((m_opus_header.version >> 6) != 0) {
271  qWarning("OpusDecoder::parseHeader(): unsupported version %d.%d",
272  (m_opus_header.version >> 6), (m_opus_header.version & 0x3F));
273  break; // unsupported version
274  }
275 
276  // number of channels
277  m_opus_header.channels = h->channels;
278  if (m_opus_header.channels < 1) {
279  qWarning("OpusDecoder::parseHeader(): channels==0");
280  break; // no channels?
281  }
282 
283  // preskip
284  m_opus_header.preskip = qFromLittleEndian<quint16>(h->preskip);
285 
286  // sample rate
287  m_opus_header.sample_rate = qFromLittleEndian<quint32>(h->sample_rate);
288 
289  // for debugging rate conversion issues:
290 // m_opus_header.sample_rate =
291 // _opus_next_sample_rate(m_opus_header.sample_rate);
292 
293  // gain
294  m_opus_header.gain = qFromLittleEndian<quint16>(h->gain);
295 
296  // channel mapping
297  m_opus_header.channel_mapping = h->channel_mapping;
298 
299  // multi stream support
300  if (m_opus_header.channel_mapping) {
301  // number of streams, must be >= 1
302  m_opus_header.streams = h->streams;
303  if (m_opus_header.streams < 1) {
304  qWarning("OpusDecoder::parseHeader(): streams==0");
305  break;
306  }
307 
308  // number of coupled
309  m_opus_header.coupled = h->coupled;
310  if (m_opus_header.coupled > m_opus_header.streams) {
311  qWarning("OpusDecoder::parseHeader(): coupled=%d > %d",
312  m_opus_header.coupled, m_opus_header.streams);
313  break; // must be <= number of streams
314  }
315  if ((m_opus_header.coupled + m_opus_header.streams) >= 256) {
316  qWarning("OpusDecoder::parseHeader(): "
317  "coupled + streams = %d (> 256)",
318  m_opus_header.coupled + m_opus_header.streams);
319  break; // must be less that 256
320  }
321 
322  // coupling map
323  unsigned int i;
324  for (i = 0; i < m_opus_header.channels; i++) {
325  quint8 c = h->map[i];
326  if (c > (m_opus_header.coupled + m_opus_header.streams)) {
327  qWarning("OpusDecoder::parseHeader(): mapping[%d]"
328  "out of range: %d (> %d)", i, c,
329  m_opus_header.coupled + m_opus_header.streams);
330  break; // mapping out of range
331  }
332  if (m_opus_header.map[i] != 0xFF) {
333  qWarning("OpusDecoder::parseHeader(): mapping[%d]"
334  "already occupied: %d", i,
335  m_opus_header.map[i]);
336  break; // mapping already occupied
337  }
338 
339  m_opus_header.map[i] = c;
340  }
341  if (i < m_opus_header.channels) break; // something went wrong
342  } else {
343  if (m_opus_header.channels > 2) {
344  qWarning("OpusDecoder::parseHeader(): channels > 2"
345  "(%d) but no mapping", m_opus_header.channels);
346  break;
347  }
348  m_opus_header.streams = 1;
349  m_opus_header.coupled = (m_opus_header.channels > 1) ? 1 : 0;
350  m_opus_header.map[0] = 0;
351  m_opus_header.map[1] = 1;
352  }
353 
354  header_ok = true;
355  } while (false);
356 
357  if (!header_ok) {
358  // error case; not an Opus header
359  Kwave::MessageBox::error(widget, i18n(
360  "This Ogg bitstream does not contain valid Opus audio data."));
361  return -1;
362  }
363 
364  // get the standard properties
365  info.setTracks(m_opus_header.channels);
366  info.setRate(m_opus_header.sample_rate);
368 
369  return 1;
370 }
Kwave::opus_header_t m_opus_header
Definition: OpusDecoder.h:140
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
void setRate(double rate)
Definition: FileInfo.cpp:424
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
void setTracks(unsigned int tracks)
Definition: FileInfo.cpp:454
ogg_packet & m_op
Definition: OpusDecoder.h:137
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parseOpusTags()

int Kwave::OpusDecoder::parseOpusTags ( QWidget *  widget,
Kwave::FileInfo info 
)
protectedvirtual

parse the "OpusTags" header of the stream

Parameters
widgeta QWidget to be used as parent for error messages
inforeference to a FileInfo to fill
Returns
-1 if failed or +1 if succeeded

Definition at line 132 of file OpusDecoder.cpp.

References DBG, Kwave::MessageBox::error(), m_og, m_op, m_os, m_oy, m_source, and parseComment().

Referenced by open(), and ~OpusDecoder().

133 {
134  bool comments_ok = false;
135  unsigned int counter = 0;
136  while (counter < 1) {
137  while(counter < 1) {
138  int result = ogg_sync_pageout(&m_oy, &m_og);
139  if (result == 0) break; // Need more data
140  if (result == 1) {
141  ogg_stream_pagein(&m_os, &m_og);
142  counter++;
143  }
144  }
145 
146  // no harm in not checking before adding more
147  char *buffer = ogg_sync_buffer(&m_oy, 4096);
148  qint64 bytes = m_source->read(buffer, 4096);
149  if (!bytes && counter < 1) {
150  Kwave::MessageBox::error(widget, i18n(
151  "End of file before finding Opus Comment headers."));
152  return -1;
153  }
154  ogg_sync_wrote(&m_oy, static_cast<long int>(bytes));
155  }
156 
157  bool first_packet = true;
158  unsigned int fields = 0;
159  while (ogg_stream_packetout(&m_os, &m_op) == 1) {
160  const unsigned char *c = m_op.packet;
161  unsigned long int length = m_op.bytes;
162 
163  // check length of comments and magic value
164  if (length < 16) {
165  qWarning("OpusDecoder::parseHeader(): comment length < 16 (%lu)",
166  length);
167  break;
168  }
169  if (memcmp(c, "OpusTags", 8) != 0) {
170  qWarning("OpusDecoder::parseHeader(): OpusTags magic not found");
171  break;
172  }
173  c += 8;
174  length -= 8;
175 
176  if (first_packet) {
177  // the start of the first packet contains the software
178 
179  // read length of the comment
180  quint32 len = qFromLittleEndian<quint32>(c);
181  c += 4;
182  length -= 4;
183  if (len > length) {
184  // comment extends beyond end of packet
185  qWarning("OpusDecoder::parseHeader(): encoder name truncated "
186  "(len=%u, max=%lu)", len, length);
187  len = static_cast<quint32>(length);
188  }
189  QString encoder =
190  QString::fromUtf8(reinterpret_cast<const char *>(c), len);
191  c += len;
192  length -= len;
193  qDebug(" Encoded with '%s'", DBG(encoder));
194  /* info.set(Kwave::INF_SOFTWARE, software); */
195 
196  if (length < 4) {
197  qWarning("OpusDecoder::parseHeader(): tag is too short (%lu)",
198  length);
199  break;
200  }
201  fields = qFromLittleEndian<quint32>(c);
202  c += 4;
203  length -= 4;
204 
205  first_packet = false;
206  }
207 
208  while (fields && (length > 4)) {
209  if (length < 4) {
210  qWarning("OpusDecoder::parseHeader(): broken comment (%lu)",
211  length);
212  break;
213  }
214  quint32 len = qFromLittleEndian<quint32>(c);
215  c += 4;
216  length -= 4;
217  if (len > length) {
218  // comment extends beyond end of packet
219  qWarning("OpusDecoder::parseHeader(): comment truncated "
220  "(len=%u, max=%lu)", len, length);
221  len = static_cast<quint32>(length);
222  }
223  QString comment =
224  QString::fromUtf8(reinterpret_cast<const char *>(c), len);
225  c += len;
226  length -= len;
227 
228  parseComment(info, comment);
229 
230  fields--;
231  }
232 
233  comments_ok = (fields == 0);
234  break;
235  }
236 
237  if (!comments_ok) {
238  qDebug("OpusDecoder: WARNING: no comment block found!?");
239  }
240 
241  return 1;
242 }
QIODevice * m_source
Definition: OpusDecoder.h:119
void parseComment(Kwave::FileInfo &info, const QString &comment)
Definition: OpusDecoder.cpp:74
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
#define DBG(qs)
Definition: String.h:55
ogg_sync_state & m_oy
Definition: OpusDecoder.h:128
ogg_stream_state & m_os
Definition: OpusDecoder.h:131
ogg_packet & m_op
Definition: OpusDecoder.h:137
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset()

void Kwave::OpusDecoder::reset ( )
virtual

reset the stream info

Implements Kwave::OggSubDecoder.

Definition at line 631 of file OpusDecoder.cpp.

References m_opus_decoder, and m_raw_buffer.

Referenced by close(), and ~OpusDecoder().

632 {
633  if (m_opus_decoder)
634  opus_multistream_decoder_destroy(m_opus_decoder);
635  m_opus_decoder = Q_NULLPTR;
636 
637  if (m_raw_buffer)
638  free(m_raw_buffer);
639  m_raw_buffer = Q_NULLPTR;
640 
641 }
OpusMSDecoder * m_opus_decoder
Definition: OpusDecoder.h:143
Here is the caller graph for this function:

Member Data Documentation

◆ m_buffer

Kwave::MultiTrackSink<Kwave::SampleBuffer, true>* Kwave::OpusDecoder::m_buffer
private

multi track buffer, for blockwise writing to the destination

Definition at line 152 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_bytes_count

quint64 Kwave::OpusDecoder::m_bytes_count
private

total number of bytes, without overhead

Definition at line 173 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_comments_map

Kwave::VorbisCommentMap Kwave::OpusDecoder::m_comments_map
private

map for translating Opus comments to Kwave FileInfo

Definition at line 146 of file OpusDecoder.h.

Referenced by parseComment().

◆ m_granule_first

qint64 Kwave::OpusDecoder::m_granule_first
private

first detected granule pos (minimum)

Definition at line 188 of file OpusDecoder.h.

Referenced by decode(), and open().

◆ m_granule_last

qint64 Kwave::OpusDecoder::m_granule_last
private

last detected granule pos (maximum)

Definition at line 191 of file OpusDecoder.h.

Referenced by decode(), and open().

◆ m_granule_offset

qint64 Kwave::OpusDecoder::m_granule_offset
private

number of samples missing at the start (in first granule)

Definition at line 194 of file OpusDecoder.h.

Referenced by decode(), and open().

◆ m_og

ogg_page& Kwave::OpusDecoder::m_og
private

one Ogg bitstream page. Opus packets are inside

Definition at line 134 of file OpusDecoder.h.

Referenced by decode(), and parseOpusTags().

◆ m_op

ogg_packet& Kwave::OpusDecoder::m_op
private

one raw packet of data for decode

Definition at line 137 of file OpusDecoder.h.

Referenced by decode(), parseOpusHead(), and parseOpusTags().

◆ m_opus_decoder

OpusMSDecoder* Kwave::OpusDecoder::m_opus_decoder
private

Opus multistream decoder object

Definition at line 143 of file OpusDecoder.h.

Referenced by decode(), open(), and reset().

◆ m_opus_header

Kwave::opus_header_t Kwave::OpusDecoder::m_opus_header
private

the Opus stream header

Definition at line 140 of file OpusDecoder.h.

Referenced by close(), decode(), open(), parseComment(), and parseOpusHead().

◆ m_os

ogg_stream_state& Kwave::OpusDecoder::m_os
private

take physical pages, weld into a logical stream of packets

Definition at line 131 of file OpusDecoder.h.

Referenced by parseOpusTags().

◆ m_output_is_connected

bool Kwave::OpusDecoder::m_output_is_connected
private

if true, the output of the rate converter or sample buffer has been connected to the decoder's sink

Definition at line 161 of file OpusDecoder.h.

Referenced by close(), and decode().

◆ m_oy

ogg_sync_state& Kwave::OpusDecoder::m_oy
private

sync and verify incoming physical bitstream

Definition at line 128 of file OpusDecoder.h.

Referenced by parseOpusTags().

◆ m_packet_count

unsigned int Kwave::OpusDecoder::m_packet_count
private

total number of packets

Definition at line 164 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_packet_len_max

int Kwave::OpusDecoder::m_packet_len_max
private

maximum detected length of a packet [samples]

Definition at line 179 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_packet_len_min

int Kwave::OpusDecoder::m_packet_len_min
private

minimum detected length of a packet [samples]

Definition at line 176 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_packet_size_max

int Kwave::OpusDecoder::m_packet_size_max
private

maximum detected size of a packet [bytes]

Definition at line 185 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_packet_size_min

int Kwave::OpusDecoder::m_packet_size_min
private

minimum detected size of a packet [bytes]

Definition at line 182 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_preskip

int Kwave::OpusDecoder::m_preskip
private

number of samples to skip at start

Definition at line 197 of file OpusDecoder.h.

Referenced by decode(), and open().

◆ m_rate_converter

Kwave::StreamObject* Kwave::OpusDecoder::m_rate_converter
private

sample rate converter (when needed)

Definition at line 155 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_raw_buffer

float* Kwave::OpusDecoder::m_raw_buffer
private

buffer for decoded raw audio data

Definition at line 149 of file OpusDecoder.h.

Referenced by decode(), open(), and reset().

◆ m_samples_raw

quint64 Kwave::OpusDecoder::m_samples_raw
private

total number of raw samples, at the input of the decoder, before rate conversion

Definition at line 170 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_samples_written

sample_index_t Kwave::OpusDecoder::m_samples_written
private

last known position in the output stream [samples]

Definition at line 125 of file OpusDecoder.h.

Referenced by close(), decode(), and open().

◆ m_source

QIODevice* Kwave::OpusDecoder::m_source
private

IO device to read from

Definition at line 119 of file OpusDecoder.h.

Referenced by open(), and parseOpusTags().

◆ m_stream_start_pos

qint64 Kwave::OpusDecoder::m_stream_start_pos
private

first stream with audio data

Definition at line 122 of file OpusDecoder.h.

Referenced by open().


The documentation for this class was generated from the following files: