24 #include <opus/opus_defines.h> 26 #include <QApplication> 32 #include <KLocalizedString> 52 #define MAX_FRAME_SIZE (960 * 6) 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),
75 const QString &comment)
78 int pos = comment.indexOf(
_(
"="));
80 qWarning(
"OpusDecoder: malformed comment: '%s'",
DBG(comment));
85 QString tag = comment.left(pos).toUpper();
86 QString val = comment.mid(pos + 1).trimmed();
89 if ((tag ==
_(
"REPLAY_TRACK_GAIN")) || (tag ==
_(
"REPLAY_ALBUM_GAIN"))) {
92 val = val.left(val.indexOf(
_(
"dB"), 0, Qt::CaseInsensitive)).trimmed();
96 int q8gain =
Kwave::toInt(rint(val.toDouble(&ok) * 256.0));
99 qDebug(
" OpusDecoder: %s %+0.1g dB",
DBG(tag),
100 static_cast<double>(q8gain) / 256.0);
103 }
else if ((tag ==
_(
"R128_TRACK_GAIN")) || (tag ==
_(
"R128_ALBUM_GAIN"))) {
109 qDebug(
" OpusDecoder: %s %+0.1g dB",
DBG(tag),
110 static_cast<double>(q8gain) / 256.0);
117 qDebug(
"unsupported tag '%s', value='%s'",
DBG(tag),
DBG(val));
125 val = info.
get(property).toString() +
_(
"; ") + val;
128 info.
set(property, val);
134 bool comments_ok =
false;
135 unsigned int counter = 0;
136 while (counter < 1) {
138 int result = ogg_sync_pageout(&
m_oy, &
m_og);
139 if (result == 0)
break;
147 char *buffer = ogg_sync_buffer(&
m_oy, 4096);
148 qint64 bytes =
m_source->read(buffer, 4096);
149 if (!bytes && counter < 1) {
151 "End of file before finding Opus Comment headers."));
154 ogg_sync_wrote(&
m_oy, static_cast<long int>(bytes));
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;
165 qWarning(
"OpusDecoder::parseHeader(): comment length < 16 (%lu)",
169 if (memcmp(c,
"OpusTags", 8) != 0) {
170 qWarning(
"OpusDecoder::parseHeader(): OpusTags magic not found");
180 quint32 len = qFromLittleEndian<quint32>(c);
185 qWarning(
"OpusDecoder::parseHeader(): encoder name truncated " 186 "(len=%u, max=%lu)", len, length);
187 len =
static_cast<quint32
>(length);
190 QString::fromUtf8(reinterpret_cast<const char *>(c), len);
193 qDebug(
" Encoded with '%s'",
DBG(encoder));
197 qWarning(
"OpusDecoder::parseHeader(): tag is too short (%lu)",
201 fields = qFromLittleEndian<quint32>(c);
205 first_packet =
false;
208 while (fields && (length > 4)) {
210 qWarning(
"OpusDecoder::parseHeader(): broken comment (%lu)",
214 quint32 len = qFromLittleEndian<quint32>(c);
219 qWarning(
"OpusDecoder::parseHeader(): comment truncated " 220 "(len=%u, max=%lu)", len, length);
221 len =
static_cast<quint32
>(length);
224 QString::fromUtf8(reinterpret_cast<const char *>(c), len);
233 comments_ok = (fields == 0);
238 qDebug(
"OpusDecoder: WARNING: no comment block found!?");
250 bool header_ok =
false;
252 if (!
m_op.b_o_s || (
m_op.bytes < 19)) {
253 qWarning(
"OpusDecoder::parseHeader(): header too short");
257 Kwave::opus_header_t *h =
258 reinterpret_cast<Kwave::opus_header_t *
>(
m_op.packet);
264 qWarning(
"OpusDecoder::parseHeader(): OpusHead magic not found");
271 qWarning(
"OpusDecoder::parseHeader(): unsupported version %d.%d",
279 qWarning(
"OpusDecoder::parseHeader(): channels==0");
284 m_opus_header.preskip = qFromLittleEndian<quint16>(h->preskip);
287 m_opus_header.sample_rate = qFromLittleEndian<quint32>(h->sample_rate);
304 qWarning(
"OpusDecoder::parseHeader(): streams==0");
311 qWarning(
"OpusDecoder::parseHeader(): coupled=%d > %d",
316 qWarning(
"OpusDecoder::parseHeader(): " 317 "coupled + streams = %d (> 256)",
325 quint8 c = h->map[i];
327 qWarning(
"OpusDecoder::parseHeader(): mapping[%d]" 328 "out of range: %d (> %d)", i, c,
333 qWarning(
"OpusDecoder::parseHeader(): mapping[%d]" 334 "already occupied: %d", i,
344 qWarning(
"OpusDecoder::parseHeader(): channels > 2" 360 "This Ogg bitstream does not contain valid Opus audio data."));
393 int err = OPUS_BAD_ARG;
407 i18n(
"Opus decoder failed"));
413 err = opus_multistream_decoder_ctl(
416 if (err == OPUS_OK) {
417 qDebug(
" OpusDecoder: gain adjusted to %d Q8dB",
434 if (rate_orig != rate_supp) {
437 qDebug(
" OpusDecoder::open(): converting sample rate: %d -> %d",
438 rate_supp, rate_orig);
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)
460 qWarning(
"OpusDecoder::open(): creating rate converter failed!");
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);
473 qint64 file_size =
m_source->size();
474 qreal bitrate = 196000;
475 qreal rate = rate_orig;
476 qreal seconds = file_size / (bitrate / 8);
479 qDebug(
" OpusDecoder: estimated length: %llu samples", samples);
511 int frames = opus_packet_get_nb_frames(
513 static_cast<opus_int32>(
m_op.bytes)
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));
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));
538 const qint64 gp =
static_cast<qint64
>(ogg_page_granulepos(&
m_og));
549 int length = opus_multistream_decode_float(
551 static_cast<const unsigned char *>(
m_op.packet),
552 static_cast<opus_int32>(
m_op.bytes),
556 qWarning(
"OpusDecoder::decode() failed: '%s'",
564 const float g = powf(10.0f,
m_opus_header.gain / (20.0f * 256.0f));
577 qWarning(
"OpusDecoder::decode() connecting output failed");
602 if (diff > length)
return 0;
607 for (
unsigned int t = 0; t < tracks; t++) {
609 const float *in = p + t;
610 for (
int frame = 0; frame < length; frame++) {
612 double noise = (drand48() - double(0.5)) /
double(
SAMPLE_MAX);
613 double d =
static_cast<double>(*in);
625 QApplication::processEvents();
650 for (
unsigned int t = 0; t < tracks; t++) {
666 qDebug(
" OpusDecoder: packet length: %d...%d samples",
668 qDebug(
" OpusDecoder: packet size: %d...%d bytes",
676 qDebug(
" OpusDecoder: hard CBR mode");
680 qDebug(
" OpusDecoder: VBR mode");
686 qDebug(
" OpusDecoder: average frame length: %0.1f ms", avg_ms);
696 qDebug(
" OpusDecoder: average bitrate: %d bits/sec", bitrate);
bool contains(const FileProperty property) const
int opus_next_sample_rate(int rate)
Kwave::StreamObject * m_rate_converter
qint64 m_stream_start_pos
unsigned int m_packet_count
virtual int decode(Kwave::MultiWriter &dst) Q_DECL_OVERRIDE
QVariant get(FileProperty key) const
Kwave::opus_header_t m_opus_header
OpusMSDecoder * m_opus_decoder
virtual int parseOpusTags(QWidget *widget, Kwave::FileInfo &info)
void parseComment(Kwave::FileInfo &info, const QString &comment)
void put(sample_t sample)
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
void set(FileProperty key, const QVariant &value)
void setRate(double rate)
static int error(QWidget *widget, QString message, QString caption=QString())
Kwave::VorbisCommentMap m_comments_map
void setTracks(unsigned int tracks)
Kwave::MultiTrackSink< Kwave::SampleBuffer, true > * m_buffer
QString opus_error(int err)
virtual void close(Kwave::FileInfo &info) Q_DECL_OVERRIDE
static sample_t double2sample(const double f)
sample_index_t m_samples_written
OpusDecoder(QIODevice *source, ogg_sync_state &oy, ogg_stream_state &os, ogg_page &og, ogg_packet &op)
bool m_output_is_connected
virtual int parseOpusHead(QWidget *widget, Kwave::FileInfo &info)
virtual SINK * at(unsigned int track) const
void setAttribute(const char *attribute, const QVariant &value)
virtual void reset() Q_DECL_OVERRIDE
virtual void dump() const Q_DECL_OVERRIDE
virtual int open(QWidget *widget, Kwave::FileInfo &info) Q_DECL_OVERRIDE