kwave  18.07.70
OpusDecoder.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  OpusDecoder.cpp - sub decoder for Opus in an Ogg container
3  -------------------
4  begin : Wed Dec 26 2012
5  copyright : (C) 2012 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <limits>
21 #include <math.h>
22 #include <stdlib.h>
23 
24 #include <opus/opus_defines.h>
25 
26 #include <QApplication>
27 #include <QDate>
28 #include <QIODevice>
29 #include <QString>
30 #include <QtEndian>
31 
32 #include <KLocalizedString>
33 
34 #include "libkwave/BitrateMode.h"
35 #include "libkwave/Compression.h"
36 #include "libkwave/Connect.h"
37 #include "libkwave/MessageBox.h"
40 #include "libkwave/MultiWriter.h"
41 #include "libkwave/Sample.h"
42 #include "libkwave/SampleArray.h"
44 #include "libkwave/Utils.h"
45 #include "libkwave/Writer.h"
47 
48 #include "OpusCommon.h"
49 #include "OpusDecoder.h"
50 
52 #define MAX_FRAME_SIZE (960 * 6)
53 
54 //***************************************************************************
56  ogg_sync_state &oy,
57  ogg_stream_state &os,
58  ogg_page &og,
59  ogg_packet &op)
60  :m_source(source), m_stream_start_pos(0), m_samples_written(0),
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),
65  m_packet_count(0), m_samples_raw(0), m_bytes_count(0),
66  m_packet_len_min(0), m_packet_len_max(0),
67  m_packet_size_min(0), m_packet_size_max(0),
68  m_granule_first(0), m_granule_last(0), m_granule_offset(0),
69  m_preskip(0)
70 {
71 }
72 
73 //***************************************************************************
75  const QString &comment)
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 }
130 
131 //***************************************************************************
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 }
243 
244 //***************************************************************************
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 }
371 
372 //***************************************************************************
373 int Kwave::OpusDecoder::open(QWidget *widget, Kwave::FileInfo &info)
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 }
501 
502 //***************************************************************************
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 }
629 
630 //***************************************************************************
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 }
642 
643 //***************************************************************************
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 }
701 
702 //***************************************************************************
703 //***************************************************************************
bool contains(const FileProperty property) const
Definition: FileInfo.cpp:354
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
virtual int decode(Kwave::MultiWriter &dst) Q_DECL_OVERRIDE
QVariant get(FileProperty key) const
Definition: FileInfo.cpp:372
#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
virtual int parseOpusTags(QWidget *widget, Kwave::FileInfo &info)
void parseComment(Kwave::FileInfo &info, const QString &comment)
Definition: OpusDecoder.cpp:74
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
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
#define MAX_FRAME_SIZE
Definition: OpusDecoder.cpp:52
Kwave::VorbisCommentMap m_comments_map
Definition: OpusDecoder.h:146
int toInt(T x)
Definition: Utils.h:127
void setTracks(unsigned int tracks)
Definition: FileInfo.cpp:454
Kwave::MultiTrackSink< Kwave::SampleBuffer, true > * m_buffer
Definition: OpusDecoder.h:152
virtual void finished()
QString opus_error(int err)
Definition: OpusCommon.cpp:45
virtual void close(Kwave::FileInfo &info) Q_DECL_OVERRIDE
static sample_t double2sample(const double f)
Definition: Sample.h:81
#define _(m)
Definition: memcpy.c:66
#define SAMPLE_MAX
Definition: Sample.h:52
sample_index_t m_samples_written
Definition: OpusDecoder.h:125
#define DBG(qs)
Definition: String.h:55
OpusDecoder(QIODevice *source, ogg_sync_state &oy, ogg_stream_state &os, ogg_page &og, ogg_packet &op)
Definition: OpusDecoder.cpp:55
FileProperty
Definition: FileInfo.h:45
ogg_sync_state & m_oy
Definition: OpusDecoder.h:128
virtual int parseOpusHead(QWidget *widget, Kwave::FileInfo &info)
virtual SINK * at(unsigned int track) const
ogg_stream_state & m_os
Definition: OpusDecoder.h:131
void setAttribute(const char *attribute, const QVariant &value)
virtual void reset() Q_DECL_OVERRIDE
qint32 sample_t
Definition: Sample.h:37
ogg_packet & m_op
Definition: OpusDecoder.h:137
virtual void dump() const Q_DECL_OVERRIDE
Definition: FileInfo.cpp:460
virtual int open(QWidget *widget, Kwave::FileInfo &info) Q_DECL_OVERRIDE