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 #include <new>
24 
25 #include <opus/opus_defines.h>
26 
27 #include <QApplication>
28 #include <QDate>
29 #include <QIODevice>
30 #include <QString>
31 #include <QtEndian>
32 
33 #include <KLocalizedString>
34 
35 #include "libkwave/BitrateMode.h"
36 #include "libkwave/Compression.h"
37 #include "libkwave/Connect.h"
38 #include "libkwave/MessageBox.h"
39 #include "libkwave/MultiStreamWriter.h"
40 #include "libkwave/MultiTrackSource.h"
41 #include "libkwave/MultiWriter.h"
42 #include "libkwave/Sample.h"
43 #include "libkwave/SampleArray.h"
44 #include "libkwave/StandardBitrates.h"
45 #include "libkwave/Utils.h"
46 #include "libkwave/Writer.h"
47 #include "libkwave/modules/RateConverter.h"
48 
49 #include "OpusCommon.h"
50 #include "OpusDecoder.h"
51 
52 /** maximum frame size in samples, 120ms at 48000 */
53 #define MAX_FRAME_SIZE (960 * 6)
54 
55 //***************************************************************************
OpusDecoder(QIODevice * source,ogg_sync_state & oy,ogg_stream_state & os,ogg_page & og,ogg_packet & op)56 Kwave::OpusDecoder::OpusDecoder(QIODevice *source,
57                                 ogg_sync_state &oy,
58                                 ogg_stream_state &os,
59                                 ogg_page &og,
60                                 ogg_packet &op)
61     :m_source(source), m_stream_start_pos(0), m_samples_written(0),
62      m_oy(oy), m_os(os), m_og(og), m_op(op), m_opus_decoder(Q_NULLPTR),
63      m_comments_map(), m_raw_buffer(Q_NULLPTR), m_buffer(Q_NULLPTR),
64      m_rate_converter(Q_NULLPTR),
65      m_output_is_connected(false),
66      m_packet_count(0), m_samples_raw(0), m_bytes_count(0),
67      m_packet_len_min(0), m_packet_len_max(0),
68      m_packet_size_min(0), m_packet_size_max(0),
69      m_granule_first(0), m_granule_last(0), m_granule_offset(0),
70      m_preskip(0)
71 {
72 }
73 
74 //***************************************************************************
parseComment(Kwave::FileInfo & info,const QString & comment)75 void Kwave::OpusDecoder::parseComment(Kwave::FileInfo &info,
76                                       const QString &comment)
77 {
78     // assume that there is a '=' between tag and value
79     int pos = comment.indexOf(_("="));
80     if (pos < 1) {
81 	qWarning("OpusDecoder: malformed comment: '%s'", DBG(comment));
82 	return;
83     }
84 
85     // split into tag and value
86     QString tag = comment.left(pos).toUpper();
87     QString val = comment.mid(pos + 1).trimmed();
88 
89     // special handling for gain
90     if ((tag == _("REPLAY_TRACK_GAIN")) || (tag == _("REPLAY_ALBUM_GAIN"))) {
91 	// gain in dB:
92 	// remove "dB" from the end
93 	val = val.left(val.indexOf(_("dB"), 0, Qt::CaseInsensitive)).trimmed();
94 
95 	// convert to a integer Q8 dB value
96 	bool ok = false;
97 	int q8gain = Kwave::toInt(rint(val.toDouble(&ok) * 256.0));
98 	if (ok && q8gain) {
99 	    m_opus_header.gain += q8gain;
100 	    qDebug("    OpusDecoder: %s %+0.1g dB", DBG(tag),
101 	           static_cast<double>(q8gain) / 256.0);
102 	    return;
103 	}
104     } else if ((tag == _("R128_TRACK_GAIN")) || (tag == _("R128_ALBUM_GAIN"))) {
105 	// R128_... already is a 7.8 integer value
106 	bool ok = false;
107 	int q8gain = Kwave::toInt(rint(val.toDouble(&ok)));
108 	if (ok && q8gain) {
109 	    m_opus_header.gain += q8gain;
110 	    qDebug("    OpusDecoder: %s %+0.1g dB", DBG(tag),
111 	           static_cast<double>(q8gain) / 256.0);
112 	    return;
113 	}
114     }
115 
116     // check for unknown properties
117     if (!m_comments_map.contains(tag)) {
118 	qDebug("unsupported tag '%s', value='%s'", DBG(tag), DBG(val));
119 	return;
120     }
121 
122     // set the new value
123     Kwave::FileProperty property = m_comments_map[tag];
124     if (info.contains(property)) {
125 	// property already exists, append it
126 	val = info.get(property).toString() + _("; ") + val;
127     }
128 
129     info.set(property, val);
130 }
131 
132 //***************************************************************************
parseOpusTags(QWidget * widget,Kwave::FileInfo & info)133 int Kwave::OpusDecoder::parseOpusTags(QWidget *widget, Kwave::FileInfo &info)
134 {
135     bool comments_ok = false;
136     unsigned int counter = 0;
137     while (counter < 1) {
138 	while(counter < 1) {
139 	    int result = ogg_sync_pageout(&m_oy, &m_og);
140 	    if (result == 0) break; // Need more data
141 	    if (result == 1) {
142 		ogg_stream_pagein(&m_os, &m_og);
143 		counter++;
144 	    }
145 	}
146 
147 	// no harm in not checking before adding more
148 	char *buffer = ogg_sync_buffer(&m_oy, 4096);
149 	qint64 bytes = m_source->read(buffer, 4096);
150 	if (!bytes && counter < 1) {
151 	    Kwave::MessageBox::error(widget, i18n(
152 	        "End of file before finding Opus Comment headers."));
153 	    return -1;
154 	}
155 	ogg_sync_wrote(&m_oy, static_cast<long int>(bytes));
156     }
157 
158     bool first_packet = true;
159     unsigned int fields = 0;
160     while (ogg_stream_packetout(&m_os, &m_op) == 1) {
161 	const unsigned char *c   = m_op.packet;
162 	unsigned long int length = m_op.bytes;
163 
164 	// check length of comments and magic value
165 	if (length < 16) {
166 	    qWarning("OpusDecoder::parseHeader(): comment length < 16 (%lu)",
167 	              length);
168 	    break;
169 	}
170 	if (memcmp(c, "OpusTags", 8) != 0) {
171 	    qWarning("OpusDecoder::parseHeader(): OpusTags magic not found");
172 	    break;
173 	}
174 	c      += 8;
175 	length -= 8; // length is [0...8] now
176 
177 	if (first_packet) {
178 	    // the start of the first packet contains the software
179 
180 	    // read length of the comment
181 	    quint32 len = qFromLittleEndian<quint32>(c);
182 	    c      += 4;
183 	    length -= 4; // length is [0...4] now
184 	    if (len > length) {
185 		// comment extends beyond end of packet
186 		qWarning("OpusDecoder::parseHeader(): encoder name truncated "
187 		         "(len=%u, max=%lu)", len, length);
188 		len = static_cast<quint32>(length);
189 	    }
190 	    QString encoder =
191 		QString::fromUtf8(reinterpret_cast<const char *>(c), len);
192 	    c      += len;
193 	    length -= len;
194 	    qDebug("    Encoded with '%s'", DBG(encoder));
195 	    /* info.set(Kwave::INF_SOFTWARE, software); */
196 
197 	    if (length < 4) {
198 		qWarning("OpusDecoder::parseHeader(): tag is too short (%lu)",
199 		         length);
200 		break;
201 	    }
202 	    fields = qFromLittleEndian<quint32>(c);
203 	    c      += 4;
204 	    length -= 4;
205 
206 	    first_packet = false;
207 	}
208 
209 	while (fields && (length > 4)) {
210 	    quint32 len = qFromLittleEndian<quint32>(c);
211 	    c      += 4;
212 	    length -= 4;
213 	    if (len > length) {
214 		// comment extends beyond end of packet
215 		qWarning("OpusDecoder::parseHeader(): comment truncated "
216 		         "(len=%u, max=%lu)", len, length);
217 		len = static_cast<quint32>(length);
218 	    }
219 	    QString comment =
220 		QString::fromUtf8(reinterpret_cast<const char *>(c), len);
221 	    c      += len;
222 	    length -= len;
223 
224 	    parseComment(info, comment);
225 
226 	    fields--;
227 	}
228 
229 	comments_ok = (fields == 0);
230 	break;
231     }
232 
233     if (!comments_ok) {
234 	qDebug("OpusDecoder: WARNING: no comment block found!?");
235     }
236 
237     return 1;
238 }
239 
240 //***************************************************************************
parseOpusHead(QWidget * widget,Kwave::FileInfo & info)241 int Kwave::OpusDecoder::parseOpusHead(QWidget *widget, Kwave::FileInfo &info)
242 {
243     memset(&m_opus_header, 0x00, sizeof(m_opus_header));
244     memset(&m_opus_header.map, 0xFF, sizeof(m_opus_header.map));
245 
246     bool header_ok = false;
247     do {
248 	if (!m_op.b_o_s || (m_op.bytes < 19)) {
249 	    qWarning("OpusDecoder::parseHeader(): header too short");
250 	    break;
251 	}
252 
253 	Kwave::opus_header_t *h =
254 	    reinterpret_cast<Kwave::opus_header_t *>(m_op.packet);
255 
256 	// magic value
257 	memcpy(&(m_opus_header.magic[0]), &(h->magic[0]),
258 	       sizeof(m_opus_header.magic));
259 	if (memcmp(&(m_opus_header.magic[0]), "OpusHead", 8) != 0) {
260 	    qWarning("OpusDecoder::parseHeader(): OpusHead magic not found");
261 	    break; // this is no Opus stream ?
262 	}
263 
264 	// version number, only major version 0 is supported
265 	m_opus_header.version = h->version;
266 	if ((m_opus_header.version >> 6) != 0) {
267 	    qWarning("OpusDecoder::parseHeader(): unsupported version %d.%d",
268 		(m_opus_header.version >> 6), (m_opus_header.version & 0x3F));
269 	    break; // unsupported version
270 	}
271 
272 	// number of channels
273 	m_opus_header.channels = h->channels;
274 	if (m_opus_header.channels < 1) {
275 	    qWarning("OpusDecoder::parseHeader(): channels==0");
276 	    break; // no channels?
277 	}
278 
279 	// preskip
280 	m_opus_header.preskip = qFromLittleEndian<quint16>(h->preskip);
281 
282 	// sample rate
283 	m_opus_header.sample_rate = qFromLittleEndian<quint32>(h->sample_rate);
284 
285 	// for debugging rate conversion issues:
286 // 	m_opus_header.sample_rate =
287 // 	    _opus_next_sample_rate(m_opus_header.sample_rate);
288 
289 	// gain
290 	m_opus_header.gain = qFromLittleEndian<quint16>(h->gain);
291 
292 	// channel mapping
293 	m_opus_header.channel_mapping = h->channel_mapping;
294 
295 	// multi stream support
296 	if (m_opus_header.channel_mapping) {
297 	    // number of streams, must be >= 1
298 	    m_opus_header.streams = h->streams;
299 	    if (m_opus_header.streams < 1) {
300 		qWarning("OpusDecoder::parseHeader(): streams==0");
301 		break;
302 	    }
303 
304 	    // number of coupled
305 	    m_opus_header.coupled = h->coupled;
306 	    if (m_opus_header.coupled > m_opus_header.streams) {
307 		qWarning("OpusDecoder::parseHeader(): coupled=%d > %d",
308 		    m_opus_header.coupled, m_opus_header.streams);
309 		break; // must be <= number of streams
310 	    }
311 	    if ((m_opus_header.coupled + m_opus_header.streams) >= 256) {
312 		qWarning("OpusDecoder::parseHeader(): "
313 		          "coupled + streams = %d (> 256)",
314 		    m_opus_header.coupled + m_opus_header.streams);
315 		break; // must be less that 256
316 	    }
317 
318 	    // coupling map
319 	    unsigned int i;
320 	    for (i = 0; i < m_opus_header.channels; i++) {
321 		quint8 c = h->map[i];
322 		if (c > (m_opus_header.coupled + m_opus_header.streams)) {
323 		    qWarning("OpusDecoder::parseHeader(): mapping[%d]"
324 		             "out of range: %d (> %d)", i, c,
325 			     m_opus_header.coupled + m_opus_header.streams);
326 		    break; // mapping out of range
327 		}
328 		if (m_opus_header.map[i] != 0xFF) {
329 		    qWarning("OpusDecoder::parseHeader(): mapping[%d]"
330 		             "already occupied: %d", i,
331 			     m_opus_header.map[i]);
332 		    break; // mapping already occupied
333 		}
334 
335 		m_opus_header.map[i] = c;
336 	    }
337 	    if (i < m_opus_header.channels) break; // something went wrong
338 	} else {
339 	    if (m_opus_header.channels > 2) {
340 		qWarning("OpusDecoder::parseHeader(): channels > 2"
341 		         "(%d) but no mapping", m_opus_header.channels);
342 		break;
343 	    }
344 	    m_opus_header.streams = 1;
345 	    m_opus_header.coupled = (m_opus_header.channels > 1) ? 1 : 0;
346 	    m_opus_header.map[0] = 0;
347 	    m_opus_header.map[1] = 1;
348 	}
349 
350 	header_ok = true;
351     } while (false);
352 
353     if (!header_ok) {
354 	// error case; not an Opus header
355 	Kwave::MessageBox::error(widget, i18n(
356 	    "This Ogg bitstream does not contain valid Opus audio data."));
357 	return -1;
358     }
359 
360     // get the standard properties
361     info.setTracks(m_opus_header.channels);
362     info.setRate(m_opus_header.sample_rate);
363     info.set(Kwave::INF_COMPRESSION, Kwave::Compression::OGG_OPUS);
364 
365     return 1;
366 }
367 
368 //***************************************************************************
open(QWidget * widget,Kwave::FileInfo & info)369 int Kwave::OpusDecoder::open(QWidget *widget, Kwave::FileInfo &info)
370 {
371     // extract the initial header from the first page and verify that the
372     // Ogg bitstream is in fact valid/usable Opus data
373     if (parseOpusHead(widget, info) < 1)
374 	return -1;
375 
376     // extract the second packet, it should contain the comments...
377     if (parseOpusTags(widget, info) < 1)
378 	return -1;
379 
380     // allocate memory for the output data
381     if (m_raw_buffer) free(m_raw_buffer);
382     m_raw_buffer = static_cast<float *>(
383 	malloc(sizeof(float) * MAX_FRAME_SIZE * m_opus_header.channels));
384     if (!m_raw_buffer) {
385 	Kwave::MessageBox::error(widget, i18n("Out of memory"));
386 	return -1;
387     }
388 
389     int err = OPUS_BAD_ARG;
390     qDebug("    sample rate = %d", m_opus_header.sample_rate);
391     m_opus_decoder = opus_multistream_decoder_create(
392         Kwave::opus_next_sample_rate(m_opus_header.sample_rate),
393         m_opus_header.channels,
394         m_opus_header.streams,
395         m_opus_header.coupled,
396         m_opus_header.map,
397         &err
398     );
399 
400     if ((err != OPUS_OK) || !m_opus_decoder) {
401 	info.dump();
402 	Kwave::MessageBox::error(widget, Kwave::opus_error(err),
403 	     i18n("Opus decoder failed"));
404 	return -1;
405     }
406 
407 #ifdef OPUS_SET_GAIN
408     if (m_opus_header.gain) {
409 	err = opus_multistream_decoder_ctl(
410 	    m_opus_decoder, OPUS_SET_GAIN(m_opus_header.gain)
411 	);
412 	if (err == OPUS_OK) {
413 	    qDebug("    OpusDecoder: gain adjusted to %d Q8dB",
414 	           m_opus_header.gain);
415 	    m_opus_header.gain = 0;
416 	}
417     }
418 #endif /* OPUS_SET_GAIN */
419 
420     const unsigned int tracks = m_opus_header.channels;
421     int rate_orig = m_opus_header.sample_rate;
422     int rate_supp = Kwave::opus_next_sample_rate(rate_orig);
423 
424     // create a multi track sample buffer
425     m_buffer = new(std::nothrow)
426 	Kwave::MultiTrackSink<Kwave::SampleBuffer, true>(tracks);
427     Q_ASSERT(m_buffer);
428     if (!m_buffer) return -1;
429 
430     // handle sample rate conversion
431     if (rate_orig != rate_supp) {
432 	bool ok = true;
433 
434 	qDebug("    OpusDecoder::open(): converting sample rate: %d -> %d",
435 	       rate_supp, rate_orig);
436 
437 	m_rate_converter = new(std::nothrow)
438 	    Kwave::MultiTrackSource<Kwave::RateConverter, true>(tracks);
439 	Q_ASSERT(m_rate_converter);
440 	if (!m_rate_converter) ok = false;
441 
442 	if (ok) {
443 	    double rate_from = static_cast<double>(rate_supp);
444 	    double rate_to   = static_cast<double>(rate_orig);
445 	    m_rate_converter->setAttribute(
446 		SLOT(setRatio(QVariant)),
447 		QVariant(rate_to / rate_from)
448 	    );
449 
450 	    ok = Kwave::connect(
451 		*m_buffer,         SIGNAL(output(Kwave::SampleArray)),
452 		*m_rate_converter, SLOT(input(Kwave::SampleArray))
453 	    );
454 	}
455 
456 	if (!ok) {
457 	    qWarning("OpusDecoder::open(): creating rate converter failed!");
458 	}
459 
460 	if (!ok) {
461 	    qDebug("OpusDecoder::open(): sample rate %d is not "
462 		   "supported but rate conversion is not available "
463 		   "-> setting to %d", rate_orig, rate_supp);
464 	    m_opus_header.sample_rate = rate_supp;
465 	}
466     }
467 
468     // estimate the length of the file from file size, bitrate, channels
469     if (!m_source->isSequential()) {
470 	qint64 file_size       = m_source->size();
471 	qreal bitrate          = 196000; // just guessed
472 	qreal rate             = rate_orig;
473 	qreal seconds          = static_cast<qreal>(file_size) / (bitrate / 8);
474 	sample_index_t samples = static_cast<sample_index_t>(seconds * rate);
475 
476 	qDebug("    OpusDecoder: estimated length: %llu samples", samples);
477 	info.set(Kwave::INF_ESTIMATED_LENGTH, samples);
478     }
479 
480     m_stream_start_pos = m_source->pos();
481     m_samples_written  = 0;
482     m_packet_count     = 0;
483     m_samples_raw      = 0;
484     m_bytes_count      = 0;
485 
486     m_packet_len_min  = std::numeric_limits<int>::max();
487     m_packet_len_max  = 0;
488     m_packet_size_min = std::numeric_limits<int>::max();
489     m_packet_size_max = 0;
490 
491     m_preskip         = m_opus_header.preskip;
492     m_granule_first   = std::numeric_limits<qint64>::max();
493     m_granule_last    = 0;
494     m_granule_offset  = 0;
495 
496     return 1;
497 }
498 
499 //***************************************************************************
decode(Kwave::MultiWriter & dst)500 int Kwave::OpusDecoder::decode(Kwave::MultiWriter &dst)
501 {
502     if (!m_opus_decoder || !m_raw_buffer || !m_buffer)
503 	return -1;
504 
505     // get some statistical data, for bitrate mode detection
506     m_packet_count++;
507 
508     int frames = opus_packet_get_nb_frames(
509 	m_op.packet,
510 	static_cast<opus_int32>(m_op.bytes)
511     );
512     if(frames < 1 || frames > 48) {
513 	qWarning("WARNING: Invalid packet TOC in packet #%llu",
514 	         static_cast<unsigned long long int>(m_op.packetno));
515     }
516     int spf = opus_packet_get_samples_per_frame(m_op.packet, 48000);
517     int spp = frames * spf;
518     if (spp < 120 || spp > 5760 || (spp % 120) != 0) {
519 	qWarning("WARNING: Invalid packet TOC in packet #%llu",
520 	         static_cast<unsigned long long int>(m_op.packetno));
521     }
522 
523     if (spp < m_packet_len_min) m_packet_len_min = spp;
524     if (spp > m_packet_len_max) m_packet_len_max = spp;
525     if (m_op.bytes < m_packet_size_min)
526 	m_packet_size_min = Kwave::toInt(m_op.bytes);
527     if (m_op.bytes > m_packet_size_max)
528 	m_packet_size_max = Kwave::toInt(m_op.bytes);
529 
530     // total count of samples and bytes
531     m_samples_raw += spp;
532     m_bytes_count += m_op.bytes;
533 
534     // granule pos handling
535     const qint64 gp = static_cast<qint64>(ogg_page_granulepos(&m_og));
536     if (gp >= 0) {
537 	if (gp < m_granule_first) m_granule_first = gp;
538 	if (gp > m_granule_last)  m_granule_last  = gp;
539 	if (m_granule_first == m_granule_last) {
540 	    // calculate how many samples might be missing at the start
541 	    m_granule_offset = m_granule_first - m_samples_raw;
542 	}
543     }
544 
545     // decode the audio data into a buffer with float values
546     int length = opus_multistream_decode_float(
547 	m_opus_decoder,
548 	static_cast<const unsigned char *>(m_op.packet),
549 	static_cast<opus_int32>(m_op.bytes),
550 	m_raw_buffer, MAX_FRAME_SIZE, 0
551     );
552     if (length <= 0) {
553 	qWarning("OpusDecoder::decode() failed: '%s'",
554 	         DBG(Kwave::opus_error(length)));
555 	return -1;
556     }
557     Q_ASSERT(length <= MAX_FRAME_SIZE);
558 
559     // manually apply the gain if necessary
560     if (m_opus_header.gain) {
561 	const float g = powf(10.0f, m_opus_header.gain / (20.0f * 256.0f));
562 	for (int i = 0; i < (length * m_opus_header.channels); i++)
563 	    m_raw_buffer[i] *= g;
564     }
565 
566     const unsigned int tracks = m_opus_header.channels;
567 
568     if (!m_output_is_connected) {
569 	Kwave::StreamObject *src =
570 	    (m_rate_converter) ? m_rate_converter : m_buffer;
571 	if (!Kwave::connect(*src, SIGNAL(output(Kwave::SampleArray)),
572 	                     dst, SLOT(input(Kwave::SampleArray))) )
573 	{
574 	    qWarning("OpusDecoder::decode() connecting output failed");
575 	    return -1;
576 	}
577 	m_output_is_connected = true;
578     }
579 
580     // handle preskip
581     const float *p = m_raw_buffer;
582     if (m_preskip) {
583 	if (m_preskip >= length) {
584 	    m_preskip -= length;
585 	    return 0; // skip the complete buffer
586 	}
587 	// shrink buffer by preskip samples
588 	length    -= m_preskip;
589 	p         += m_preskip * tracks;
590 	m_preskip  = 0;
591     }
592 
593     // check trailing data at the end (after the granule)
594     const sample_index_t last = (m_granule_last - m_granule_offset) -
595 	m_opus_header.preskip;
596 
597     if ((m_samples_written + length) > last) {
598 	int diff = Kwave::toInt((m_samples_written + length) - last);
599 	if (diff > length) return 0;
600 	length -= diff;
601     }
602 
603     // convert the buffer from float to sample_t, blockwise...
604     for (unsigned int t = 0; t < tracks; t++) {
605 	Kwave::SampleBuffer *buffer = m_buffer->at(t);
606 	const float         *in     = p + t;
607 	for (int frame = 0; frame < length; frame++) {
608 	    // scale, use some primitive noise shaping + clipping
609 	    double   noise = (drand48() - double(0.5)) / double(SAMPLE_MAX);
610 	    double   d     = static_cast<double>(*in);
611 	    sample_t s     = qBound<sample_t>(
612 		SAMPLE_MIN, double2sample(d + noise), SAMPLE_MAX
613 	    );
614 	    buffer->put(s);
615 	    in += tracks;
616 	}
617     }
618 
619     m_samples_written += length;
620 
621     // update the progress bar
622     QApplication::processEvents();
623 
624     return 0;
625 }
626 
627 //***************************************************************************
reset()628 void Kwave::OpusDecoder::reset()
629 {
630     if (m_opus_decoder)
631 	opus_multistream_decoder_destroy(m_opus_decoder);
632     m_opus_decoder = Q_NULLPTR;
633 
634     if (m_raw_buffer)
635 	free(m_raw_buffer);
636     m_raw_buffer = Q_NULLPTR;
637 
638 }
639 
640 //***************************************************************************
close(Kwave::FileInfo & info)641 void Kwave::OpusDecoder::close(Kwave::FileInfo &info)
642 {
643     // flush the buffer of the sample rate converter, to avoid missing
644     // samples due to the limitations of libsamplerate
645     if (m_buffer) {
646 	const unsigned int tracks = m_opus_header.channels;
647 	for (unsigned int t = 0; t < tracks; t++) {
648 	    Kwave::SampleBuffer *buffer = m_buffer->at(t);
649 	    Q_ASSERT(buffer);
650 	    buffer->finished();
651 	}
652     }
653 
654     if (m_buffer) delete m_buffer;
655     m_buffer = Q_NULLPTR;
656 
657     delete m_rate_converter;
658     m_rate_converter = Q_NULLPTR;
659 
660     m_output_is_connected = false;
661 
662     qDebug("    OpusDecoder: packet count=%u", m_packet_count);
663     qDebug("    OpusDecoder: packet length: %d...%d samples",
664 	   m_packet_len_min, m_packet_len_max);
665     qDebug("    OpusDecoder: packet size: %d...%d bytes",
666 	   m_packet_size_min, m_packet_size_max);
667 
668     if ( (m_packet_len_min == m_packet_len_max) &&
669          (m_packet_size_min == m_packet_size_max) )
670     {
671 	// detected hard CBR mode
672 	info.set(INF_BITRATE_MODE, Kwave::BITRATE_MODE_CBR_HARD);
673 	qDebug("    OpusDecoder: hard CBR mode");
674     } else {
675 	// otherwise default to VBR mode
676 	info.set(INF_BITRATE_MODE, Kwave::BITRATE_MODE_VBR);
677 	qDebug("    OpusDecoder: VBR mode");
678     }
679 
680     // determine the avarage frame length in ms
681     qreal avg_ms = (static_cast<qreal>(m_samples_raw) /
682                     static_cast<qreal>(m_packet_count)) / 48.0;
683     qDebug("    OpusDecoder: average frame length: %0.1f ms", avg_ms);
684     info.set(INF_OPUS_FRAME_LEN, QVariant(avg_ms));
685 
686     // calculate the bitrate == n_bits / n_seconds
687     // n_bits = n_bytes * 8
688     // n_seconds = n_samples / sample_rate
689     // => bitrate = (n_bytes * 8) / (n_samples / sample_rate)
690     const double sr = Kwave::opus_next_sample_rate(m_opus_header.sample_rate);
691     int bitrate = Kwave::toInt((static_cast<double>(m_bytes_count * 8) * sr) /
692                                 static_cast<double>(m_samples_written));
693     qDebug("    OpusDecoder: average bitrate: %d bits/sec", bitrate);
694     info.set(INF_BITRATE_NOMINAL, QVariant(bitrate));
695 
696     reset();
697 }
698 
699 //***************************************************************************
700 //***************************************************************************
701