annotate data/fileio/MP3FileReader.cpp @ 1881:b504df98c3be

Ensure completion on output model is started at zero, so if it's checked before the input model has become ready and the transform has begun, it is not accidentally reported as complete (affected re-aligning models in Sonic Lineup when replacing the session)
author Chris Cannam
date Fri, 26 Jun 2020 11:45:39 +0100
parents 14747f24ad04
children
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@148 7 This file copyright 2006 Chris Cannam.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #ifdef HAVE_MAD
Chris@148 17
Chris@148 18 #include "MP3FileReader.h"
Chris@392 19 #include "base/ProgressReporter.h"
Chris@1858 20 #include "base/Profiler.h"
Chris@357 21
Chris@150 22 #include "system/System.h"
Chris@148 23
Chris@148 24 #include <sys/types.h>
Chris@148 25 #include <sys/stat.h>
Chris@148 26 #include <fcntl.h>
Chris@148 27
Chris@148 28 #include <iostream>
Chris@148 29
Chris@405 30 #include <cstdlib>
Chris@405 31
Chris@271 32 #ifdef HAVE_ID3TAG
Chris@271 33 #include <id3tag.h>
Chris@271 34 #endif
Chris@1168 35
Chris@1364 36 #ifdef _WIN32
Chris@1364 37 #include <io.h>
Chris@1364 38 #include <fcntl.h>
Chris@1364 39 #else
Chris@1364 40 #include <fcntl.h>
Chris@1364 41 #include <unistd.h>
Chris@1364 42 #endif
Chris@1364 43
Chris@186 44 #include <QFileInfo>
Chris@148 45
Chris@1346 46 #include <QTextCodec>
Chris@1346 47
Chris@1305 48 using std::string;
Chris@1305 49
Chris@1305 50 static sv_frame_t DEFAULT_DECODER_DELAY = 529;
Chris@1305 51
Chris@317 52 MP3FileReader::MP3FileReader(FileSource source, DecodeMode decodeMode,
Chris@1305 53 CacheMode mode, GaplessMode gaplessMode,
Chris@1305 54 sv_samplerate_t targetRate,
Chris@920 55 bool normalised,
Chris@392 56 ProgressReporter *reporter) :
Chris@920 57 CodedAudioFileReader(mode, targetRate, normalised),
Chris@316 58 m_source(source),
Chris@316 59 m_path(source.getLocalFilename()),
Chris@1305 60 m_gaplessMode(gaplessMode),
Chris@1284 61 m_decodeErrorShown(false),
Chris@1582 62 m_decodeThread(nullptr)
Chris@148 63 {
Chris@1279 64 SVDEBUG << "MP3FileReader: local path: \"" << m_path
Chris@1279 65 << "\", decode mode: " << decodeMode << " ("
Chris@1279 66 << (decodeMode == DecodeAtOnce ? "DecodeAtOnce" : "DecodeThreaded")
Chris@1279 67 << ")" << endl;
Chris@1279 68
Chris@148 69 m_channelCount = 0;
Chris@297 70 m_fileRate = 0;
Chris@148 71 m_fileSize = 0;
Chris@148 72 m_bitrateNum = 0;
Chris@148 73 m_bitrateDenom = 0;
Chris@148 74 m_cancelled = false;
Chris@1288 75 m_mp3FrameCount = 0;
Chris@265 76 m_completion = 0;
Chris@263 77 m_done = false;
Chris@392 78 m_reporter = reporter;
Chris@148 79
Chris@1313 80 if (m_gaplessMode == GaplessMode::Gapless) {
Chris@1307 81 CodedAudioFileReader::setFramesToTrim(DEFAULT_DECODER_DELAY, 0);
Chris@1305 82 }
Chris@1305 83
Chris@1348 84 m_fileSize = 0;
Chris@148 85
Chris@1582 86 m_fileBuffer = nullptr;
Chris@1290 87 m_fileBufferSize = 0;
Chris@1290 88
Chris@1582 89 m_sampleBuffer = nullptr;
Chris@1290 90 m_sampleBufferSize = 0;
Chris@977 91
Chris@1348 92 QFile qfile(m_path);
Chris@1348 93 if (!qfile.open(QIODevice::ReadOnly)) {
Chris@1342 94 m_error = QString("Failed to open file %1 for reading.").arg(m_path);
Chris@1342 95 SVDEBUG << "MP3FileReader: " << m_error << endl;
Chris@1342 96 return;
Chris@1343 97 }
Chris@148 98
Chris@1348 99 m_fileSize = qfile.size();
Chris@1348 100
Chris@148 101 try {
Chris@1290 102 // We need a mysterious MAD_BUFFER_GUARD (== 8) zero bytes at
Chris@1290 103 // end of input, to ensure libmad decodes the last frame
Chris@1290 104 // correctly. Otherwise the decoded audio is truncated.
Chris@1401 105 SVDEBUG << "file size = " << m_fileSize << ", buffer guard = " << MAD_BUFFER_GUARD << endl;
Chris@1290 106 m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD;
Chris@1290 107 m_fileBuffer = new unsigned char[m_fileBufferSize];
Chris@1290 108 memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD);
Chris@148 109 } catch (...) {
Chris@290 110 m_error = QString("Out of memory");
Chris@1342 111 SVDEBUG << "MP3FileReader: " << m_error << endl;
Chris@1342 112 return;
Chris@148 113 }
Chris@1348 114
Chris@1348 115 auto amountRead = qfile.read(reinterpret_cast<char *>(m_fileBuffer),
Chris@1348 116 m_fileSize);
Chris@1348 117
Chris@1348 118 if (amountRead < m_fileSize) {
Chris@1348 119 SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
Chris@1348 120 .arg(amountRead).arg(m_fileSize) << endl;
Chris@1348 121 memset(m_fileBuffer + amountRead, 0, m_fileSize - amountRead);
Chris@1348 122 m_fileSize = amountRead;
Chris@148 123 }
Chris@1348 124
Chris@1348 125 loadTags(qfile.handle());
Chris@148 126
Chris@1348 127 qfile.close();
Chris@271 128
Chris@263 129 if (decodeMode == DecodeAtOnce) {
Chris@263 130
Chris@392 131 if (m_reporter) {
Chris@392 132 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
Chris@392 133 m_reporter->setMessage
Chris@392 134 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
Chris@327 135 }
Chris@148 136
Chris@1290 137 if (!decode(m_fileBuffer, m_fileBufferSize)) {
Chris@316 138 m_error = QString("Failed to decode file %1.").arg(m_path);
Chris@263 139 }
Chris@1402 140
Chris@1402 141 if (m_sampleBuffer) {
Chris@1402 142 for (int c = 0; c < m_channelCount; ++c) {
Chris@1402 143 delete[] m_sampleBuffer[c];
Chris@1402 144 }
Chris@1402 145 delete[] m_sampleBuffer;
Chris@1582 146 m_sampleBuffer = nullptr;
Chris@1402 147 }
Chris@263 148
Chris@1290 149 delete[] m_fileBuffer;
Chris@1582 150 m_fileBuffer = nullptr;
Chris@148 151
Chris@263 152 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@398 153 endSerialised();
Chris@263 154
Chris@392 155 } else {
Chris@263 156
Chris@392 157 if (m_reporter) m_reporter->setProgress(100);
Chris@263 158
Chris@263 159 m_decodeThread = new DecodeThread(this);
Chris@263 160 m_decodeThread->start();
Chris@263 161
Chris@386 162 while ((m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0)
Chris@386 163 && !m_done) {
Chris@263 164 usleep(10);
Chris@263 165 }
Chris@386 166
Chris@1288 167 SVDEBUG << "MP3FileReader: decoding startup complete, file rate = " << m_fileRate << endl;
Chris@148 168 }
Chris@499 169
Chris@499 170 if (m_error != "") {
Chris@1279 171 SVDEBUG << "MP3FileReader::MP3FileReader(\"" << m_path << "\"): ERROR: " << m_error << endl;
Chris@499 172 }
Chris@148 173 }
Chris@148 174
Chris@148 175 MP3FileReader::~MP3FileReader()
Chris@148 176 {
Chris@1858 177 Profiler profiler("MP3FileReader::~MP3FileReader");
Chris@1858 178
Chris@263 179 if (m_decodeThread) {
Chris@265 180 m_cancelled = true;
Chris@263 181 m_decodeThread->wait();
Chris@263 182 delete m_decodeThread;
Chris@263 183 }
Chris@148 184 }
Chris@148 185
Chris@263 186 void
Chris@392 187 MP3FileReader::cancelled()
Chris@392 188 {
Chris@392 189 m_cancelled = true;
Chris@392 190 }
Chris@392 191
Chris@392 192 void
Chris@1348 193 MP3FileReader::loadTags(int fd)
Chris@271 194 {
Chris@271 195 m_title = "";
Chris@271 196
Chris@271 197 #ifdef HAVE_ID3TAG
Chris@271 198
Chris@1364 199 #ifdef _WIN32
Chris@1364 200 int id3fd = _dup(fd);
Chris@1364 201 #else
Chris@1364 202 int id3fd = dup(fd);
Chris@1364 203 #endif
Chris@1364 204
Chris@1364 205 id3_file *file = id3_file_fdopen(id3fd, ID3_FILE_MODE_READONLY);
Chris@271 206 if (!file) return;
Chris@271 207
Chris@273 208 // We can do this a lot more elegantly, but we'll leave that for
Chris@273 209 // when we implement support for more than just the one tag!
Chris@273 210
Chris@271 211 id3_tag *tag = id3_file_tag(file);
Chris@273 212 if (!tag) {
Chris@1279 213 SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl;
Chris@1364 214 id3_file_close(file); // also closes our dup'd fd
Chris@273 215 return;
Chris@271 216 }
Chris@271 217
Chris@333 218 m_title = loadTag(tag, "TIT2"); // work title
Chris@333 219 if (m_title == "") m_title = loadTag(tag, "TIT1");
Chris@1287 220 if (m_title == "") SVDEBUG << "MP3FileReader::loadTags: No title found" << endl;
Chris@273 221
Chris@333 222 m_maker = loadTag(tag, "TPE1"); // "lead artist"
Chris@333 223 if (m_maker == "") m_maker = loadTag(tag, "TPE2");
Chris@1287 224 if (m_maker == "") SVDEBUG << "MP3FileReader::loadTags: No artist/maker found" << endl;
Chris@273 225
Chris@632 226 for (unsigned int i = 0; i < tag->nframes; ++i) {
Chris@632 227 if (tag->frames[i]) {
Chris@632 228 QString value = loadTag(tag, tag->frames[i]->id);
Chris@1287 229 if (value != "") {
Chris@1287 230 m_tags[tag->frames[i]->id] = value;
Chris@1287 231 }
Chris@632 232 }
Chris@632 233 }
Chris@632 234
Chris@1364 235 id3_file_close(file); // also closes our dup'd fd
Chris@273 236
Chris@273 237 #else
Chris@1287 238 SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in" << endl;
Chris@273 239 #endif
Chris@333 240 }
Chris@273 241
Chris@333 242 QString
Chris@333 243 MP3FileReader::loadTag(void *vtag, const char *name)
Chris@333 244 {
Chris@333 245 #ifdef HAVE_ID3TAG
Chris@333 246 id3_tag *tag = (id3_tag *)vtag;
Chris@333 247
Chris@333 248 id3_frame *frame = id3_tag_findframe(tag, name, 0);
Chris@333 249 if (!frame) {
Chris@1287 250 SVDEBUG << "MP3FileReader::loadTag: No \"" << name << "\" frame found in ID3 tag" << endl;
Chris@333 251 return "";
Chris@333 252 }
Chris@333 253
Chris@333 254 if (frame->nfields < 2) {
Chris@1287 255 cerr << "MP3FileReader::loadTag: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << endl;
Chris@333 256 return "";
Chris@333 257 }
Chris@333 258
Chris@333 259 unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
Chris@333 260 if (nstrings == 0) {
Chris@1287 261 SVDEBUG << "MP3FileReader::loadTag: No strings for \"" << name << "\" in ID3 tag" << endl;
Chris@333 262 return "";
Chris@333 263 }
Chris@333 264
Chris@333 265 id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
Chris@333 266 if (!ustr) {
Chris@1287 267 SVDEBUG << "MP3FileReader::loadTag: Invalid or absent data for \"" << name << "\" in ID3 tag" << endl;
Chris@333 268 return "";
Chris@333 269 }
Chris@333 270
Chris@333 271 id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
Chris@333 272 if (!u8str) {
Chris@1287 273 SVDEBUG << "MP3FileReader::loadTag: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 tag" << endl;
Chris@333 274 return "";
Chris@333 275 }
Chris@333 276
Chris@333 277 QString rv = QString::fromUtf8((const char *)u8str);
Chris@333 278 free(u8str);
Chris@334 279
Chris@1287 280 SVDEBUG << "MP3FileReader::loadTag: Tag \"" << name << "\" -> \""
Chris@1287 281 << rv << "\"" << endl;
Chris@334 282
Chris@333 283 return rv;
Chris@333 284
Chris@333 285 #else
Chris@333 286 return "";
Chris@333 287 #endif
Chris@271 288 }
Chris@271 289
Chris@271 290 void
Chris@263 291 MP3FileReader::DecodeThread::run()
Chris@263 292 {
Chris@1290 293 if (!m_reader->decode(m_reader->m_fileBuffer, m_reader->m_fileBufferSize)) {
Chris@290 294 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path);
Chris@263 295 }
Chris@263 296
Chris@1290 297 delete[] m_reader->m_fileBuffer;
Chris@1582 298 m_reader->m_fileBuffer = nullptr;
Chris@297 299
Chris@1290 300 if (m_reader->m_sampleBuffer) {
Chris@929 301 for (int c = 0; c < m_reader->m_channelCount; ++c) {
Chris@1290 302 delete[] m_reader->m_sampleBuffer[c];
Chris@297 303 }
Chris@1290 304 delete[] m_reader->m_sampleBuffer;
Chris@1582 305 m_reader->m_sampleBuffer = nullptr;
Chris@297 306 }
Chris@297 307
Chris@1858 308 if (m_reader->isDecodeCacheInitialised()) {
Chris@1858 309 m_reader->finishDecodeCache();
Chris@1858 310 }
Chris@263 311
Chris@263 312 m_reader->m_done = true;
Chris@265 313 m_reader->m_completion = 100;
Chris@297 314
Chris@297 315 m_reader->endSerialised();
Chris@263 316 }
Chris@263 317
Chris@148 318 bool
Chris@1038 319 MP3FileReader::decode(void *mm, sv_frame_t sz)
Chris@148 320 {
Chris@148 321 DecoderData data;
Chris@148 322 struct mad_decoder decoder;
Chris@148 323
Chris@148 324 data.start = (unsigned char const *)mm;
Chris@1288 325 data.length = sz;
Chris@1310 326 data.finished = false;
Chris@148 327 data.reader = this;
Chris@148 328
Chris@1288 329 mad_decoder_init(&decoder, // decoder to initialise
Chris@1288 330 &data, // our own data block for callbacks
Chris@1288 331 input_callback, // provides (entire) input to mad
Chris@1582 332 nullptr, // checks header
Chris@1288 333 filter_callback, // filters frame before decoding
Chris@1288 334 output_callback, // receives decoded output
Chris@1288 335 error_callback, // handles decode errors
Chris@1582 336 nullptr); // "message_func"
Chris@1288 337
Chris@148 338 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
Chris@148 339 mad_decoder_finish(&decoder);
Chris@148 340
Chris@1288 341 SVDEBUG << "MP3FileReader: Decoding complete, decoded " << m_mp3FrameCount
Chris@1288 342 << " mp3 frames" << endl;
Chris@1288 343
Chris@263 344 m_done = true;
Chris@148 345 return true;
Chris@148 346 }
Chris@148 347
Chris@148 348 enum mad_flow
Chris@1288 349 MP3FileReader::input_callback(void *dp, struct mad_stream *stream)
Chris@148 350 {
Chris@148 351 DecoderData *data = (DecoderData *)dp;
Chris@148 352
Chris@1310 353 if (!data->length) {
Chris@1310 354 data->finished = true;
Chris@1310 355 return MAD_FLOW_STOP;
Chris@1310 356 }
Chris@348 357
Chris@348 358 unsigned char const *start = data->start;
Chris@1288 359 sv_frame_t length = data->length;
Chris@348 360
Chris@348 361 #ifdef HAVE_ID3TAG
Chris@1288 362 while (length > ID3_TAG_QUERYSIZE) {
Chris@1038 363 ssize_t taglen = id3_tag_query(start, ID3_TAG_QUERYSIZE);
Chris@1288 364 if (taglen <= 0) {
Chris@1288 365 break;
Chris@348 366 }
Chris@1288 367 SVDEBUG << "MP3FileReader: ID3 tag length to skip: " << taglen << endl;
Chris@1288 368 start += taglen;
Chris@1288 369 length -= taglen;
Chris@348 370 }
Chris@348 371 #endif
Chris@348 372
Chris@348 373 mad_stream_buffer(stream, start, length);
Chris@148 374 data->length = 0;
Chris@148 375
Chris@148 376 return MAD_FLOW_CONTINUE;
Chris@148 377 }
Chris@148 378
Chris@148 379 enum mad_flow
Chris@1288 380 MP3FileReader::filter_callback(void *dp,
Chris@1288 381 struct mad_stream const *stream,
Chris@1288 382 struct mad_frame *frame)
Chris@1288 383 {
Chris@1288 384 DecoderData *data = (DecoderData *)dp;
Chris@1288 385 return data->reader->filter(stream, frame);
Chris@1288 386 }
Chris@1288 387
Chris@1307 388 static string toMagic(unsigned long fourcc)
Chris@1305 389 {
Chris@1305 390 string magic("....");
Chris@1305 391 for (int i = 0; i < 4; ++i) {
Chris@1305 392 magic[3-i] = char((fourcc >> (8*i)) & 0xff);
Chris@1305 393 }
Chris@1305 394 return magic;
Chris@1305 395 }
Chris@1305 396
Chris@1288 397 enum mad_flow
Chris@1288 398 MP3FileReader::filter(struct mad_stream const *stream,
Chris@1288 399 struct mad_frame *)
Chris@1288 400 {
Chris@1289 401 if (m_mp3FrameCount > 0) {
Chris@1305 402 // only handle info frame if it appears as first mp3 frame
Chris@1289 403 return MAD_FLOW_CONTINUE;
Chris@1305 404 }
Chris@1305 405
Chris@1313 406 if (m_gaplessMode == GaplessMode::Gappy) {
Chris@1305 407 // Our non-gapless mode does not even filter out the Xing/LAME
Chris@1305 408 // frame. That's because the main reason non-gapless mode
Chris@1305 409 // exists is for backward compatibility with MP3FileReader
Chris@1305 410 // behaviour before the gapless support was added, so we even
Chris@1305 411 // need to keep the spurious 1152 samples resulting from
Chris@1305 412 // feeding Xing/LAME frame to the decoder as otherwise we'd
Chris@1305 413 // have different output from before.
Chris@1305 414 SVDEBUG << "MP3FileReader: Not gapless mode, not checking Xing/LAME frame"
Chris@1305 415 << endl;
Chris@1305 416 return MAD_FLOW_CONTINUE;
Chris@1305 417 }
Chris@1305 418
Chris@1305 419 struct mad_bitptr ptr = stream->anc_ptr;
Chris@1305 420 string magic = toMagic(mad_bit_read(&ptr, 32));
Chris@1305 421
Chris@1305 422 if (magic == "Xing" || magic == "Info") {
Chris@1305 423
Chris@1305 424 SVDEBUG << "MP3FileReader: Found Xing/LAME metadata frame (magic = \""
Chris@1305 425 << magic << "\")" << endl;
Chris@1305 426
Chris@1305 427 // All we want at this point is the LAME encoder delay and
Chris@1305 428 // padding values. We expect to see the Xing/Info magic (which
Chris@1305 429 // we've already read), then 116 bytes of Xing data, then LAME
Chris@1305 430 // magic, 5 byte version string, 12 bytes of LAME data that we
Chris@1305 431 // aren't currently interested in, then the delays encoded as
Chris@1305 432 // two 12-bit numbers into three bytes.
Chris@1305 433 //
Chris@1305 434 // (See gabriel.mp3-tech.org/mp3infotag.html)
Chris@1305 435
Chris@1305 436 for (int skip = 0; skip < 116; ++skip) {
Chris@1305 437 (void)mad_bit_read(&ptr, 8);
Chris@1305 438 }
Chris@1305 439
Chris@1305 440 magic = toMagic(mad_bit_read(&ptr, 32));
Chris@1305 441
Chris@1305 442 if (magic == "LAME") {
Chris@1305 443
Chris@1305 444 SVDEBUG << "MP3FileReader: Found LAME-specific metadata" << endl;
Chris@1305 445
Chris@1305 446 for (int skip = 0; skip < 5 + 12; ++skip) {
Chris@1305 447 (void)mad_bit_read(&ptr, 8);
Chris@1305 448 }
Chris@1305 449
Chris@1307 450 auto delay = mad_bit_read(&ptr, 12);
Chris@1307 451 auto padding = mad_bit_read(&ptr, 12);
Chris@1305 452
Chris@1305 453 sv_frame_t delayToDrop = DEFAULT_DECODER_DELAY + delay;
Chris@1305 454 sv_frame_t paddingToDrop = padding - DEFAULT_DECODER_DELAY;
Chris@1305 455 if (paddingToDrop < 0) paddingToDrop = 0;
Chris@1305 456
Chris@1305 457 SVDEBUG << "MP3FileReader: LAME encoder delay = " << delay
Chris@1305 458 << ", padding = " << padding << endl;
Chris@1305 459
Chris@1305 460 SVDEBUG << "MP3FileReader: Will be trimming " << delayToDrop
Chris@1306 461 << " samples from start and " << paddingToDrop
Chris@1306 462 << " from end" << endl;
Chris@1305 463
Chris@1307 464 CodedAudioFileReader::setFramesToTrim(delayToDrop, paddingToDrop);
Chris@1305 465
Chris@1305 466 } else {
Chris@1305 467 SVDEBUG << "MP3FileReader: Xing frame has no LAME metadata" << endl;
Chris@1305 468 }
Chris@1305 469
Chris@1305 470 return MAD_FLOW_IGNORE;
Chris@1305 471
Chris@1288 472 } else {
Chris@1305 473 return MAD_FLOW_CONTINUE;
Chris@1288 474 }
Chris@1288 475 }
Chris@1288 476
Chris@1288 477 enum mad_flow
Chris@1288 478 MP3FileReader::output_callback(void *dp,
Chris@1288 479 struct mad_header const *header,
Chris@1288 480 struct mad_pcm *pcm)
Chris@148 481 {
Chris@148 482 DecoderData *data = (DecoderData *)dp;
Chris@148 483 return data->reader->accept(header, pcm);
Chris@148 484 }
Chris@148 485
Chris@148 486 enum mad_flow
Chris@148 487 MP3FileReader::accept(struct mad_header const *header,
Chris@1343 488 struct mad_pcm *pcm)
Chris@148 489 {
Chris@148 490 int channels = pcm->channels;
Chris@148 491 int frames = pcm->length;
Chris@1288 492
Chris@148 493 if (header) {
Chris@1038 494 m_bitrateNum = m_bitrateNum + double(header->bitrate);
Chris@148 495 m_bitrateDenom ++;
Chris@148 496 }
Chris@148 497
Chris@148 498 if (frames < 1) return MAD_FLOW_CONTINUE;
Chris@148 499
Chris@148 500 if (m_channelCount == 0) {
Chris@297 501
Chris@297 502 m_fileRate = pcm->samplerate;
Chris@148 503 m_channelCount = channels;
Chris@297 504
Chris@1354 505 SVDEBUG << "MP3FileReader::accept: file rate = " << pcm->samplerate
Chris@1354 506 << ", channel count = " << channels << ", about to init "
Chris@1354 507 << "decode cache" << endl;
Chris@1354 508
Chris@297 509 initialiseDecodeCache();
Chris@297 510
Chris@297 511 if (m_cacheMode == CacheInTemporaryFile) {
Chris@690 512 // SVDEBUG << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << endl;
Chris@1858 513 startSerialised("MP3FileReader::Decode", &m_cancelled);
Chris@1858 514 if (m_cancelled) {
Chris@1858 515 return MAD_FLOW_STOP;
Chris@1858 516 }
Chris@297 517 }
Chris@148 518 }
Chris@148 519
Chris@148 520 if (m_bitrateDenom > 0) {
Chris@148 521 double bitrate = m_bitrateNum / m_bitrateDenom;
Chris@148 522 double duration = double(m_fileSize * 8) / bitrate;
Chris@148 523 double elapsed = double(m_frameCount) / m_sampleRate;
Chris@375 524 double percent = 100;
Chris@375 525 if (duration > 0.0) percent = ((elapsed * 100.0) / duration);
Chris@357 526 int p = int(percent);
Chris@357 527 if (p < 1) p = 1;
Chris@357 528 if (p > 99) p = 99;
Chris@392 529 if (m_completion != p && m_reporter) {
Chris@357 530 m_completion = p;
Chris@392 531 m_reporter->setProgress(m_completion);
Chris@148 532 }
Chris@148 533 }
Chris@148 534
Chris@1290 535 if (m_cancelled) {
Chris@1290 536 SVDEBUG << "MP3FileReader: Decoding cancelled" << endl;
Chris@1290 537 return MAD_FLOW_STOP;
Chris@1290 538 }
Chris@148 539
Chris@148 540 if (!isDecodeCacheInitialised()) {
Chris@1354 541 SVDEBUG << "MP3FileReader::accept: fallback case: file rate = " << pcm->samplerate
Chris@1354 542 << ", channel count = " << channels << ", about to init "
Chris@1354 543 << "decode cache" << endl;
Chris@148 544 initialiseDecodeCache();
Chris@148 545 }
Chris@148 546
Chris@1290 547 if (m_sampleBufferSize < size_t(frames)) {
Chris@1290 548 if (!m_sampleBuffer) {
Chris@1290 549 m_sampleBuffer = new float *[channels];
Chris@297 550 for (int c = 0; c < channels; ++c) {
Chris@1582 551 m_sampleBuffer[c] = nullptr;
Chris@297 552 }
Chris@297 553 }
Chris@297 554 for (int c = 0; c < channels; ++c) {
Chris@1290 555 delete[] m_sampleBuffer[c];
Chris@1290 556 m_sampleBuffer[c] = new float[frames];
Chris@297 557 }
Chris@1290 558 m_sampleBufferSize = frames;
Chris@297 559 }
Chris@148 560
Chris@297 561 int activeChannels = int(sizeof(pcm->samples) / sizeof(pcm->samples[0]));
Chris@297 562
Chris@297 563 for (int ch = 0; ch < channels; ++ch) {
Chris@297 564
Chris@297 565 for (int i = 0; i < frames; ++i) {
Chris@297 566
Chris@1343 567 mad_fixed_t sample = 0;
Chris@1343 568 if (ch < activeChannels) {
Chris@1343 569 sample = pcm->samples[ch][i];
Chris@1343 570 }
Chris@1343 571 float fsample = float(sample) / float(MAD_F_ONE);
Chris@297 572
Chris@1290 573 m_sampleBuffer[ch][i] = fsample;
Chris@1343 574 }
Chris@148 575 }
Chris@148 576
Chris@1290 577 addSamplesToDecodeCache(m_sampleBuffer, frames);
Chris@148 578
Chris@1288 579 ++m_mp3FrameCount;
Chris@1288 580
Chris@148 581 return MAD_FLOW_CONTINUE;
Chris@148 582 }
Chris@148 583
Chris@148 584 enum mad_flow
Chris@1288 585 MP3FileReader::error_callback(void *dp,
Chris@1288 586 struct mad_stream *stream,
Chris@1288 587 struct mad_frame *)
Chris@148 588 {
Chris@1284 589 DecoderData *data = (DecoderData *)dp;
Chris@1290 590
Chris@1312 591 sv_frame_t ix = stream->this_frame - data->start;
Chris@1312 592
Chris@1290 593 if (stream->error == MAD_ERROR_LOSTSYNC &&
Chris@1312 594 (data->finished || ix >= data->length)) {
Chris@1310 595 // We are at end of file, losing sync is expected behaviour,
Chris@1310 596 // don't report it
Chris@1290 597 return MAD_FLOW_CONTINUE;
Chris@1290 598 }
Chris@1290 599
Chris@1284 600 if (!data->reader->m_decodeErrorShown) {
Chris@1284 601 char buffer[256];
Chris@1284 602 snprintf(buffer, 255,
cannam@1334 603 "MP3 decoding error 0x%04x (%s) at byte offset %lld",
Chris@1335 604 stream->error, mad_stream_errorstr(stream), (long long int)ix);
Chris@1284 605 SVCERR << "Warning: in file \"" << data->reader->m_path << "\": "
Chris@1284 606 << buffer << " (continuing; will not report any further decode errors for this file)" << endl;
Chris@1284 607 data->reader->m_decodeErrorShown = true;
Chris@1279 608 }
Chris@148 609
Chris@148 610 return MAD_FLOW_CONTINUE;
Chris@148 611 }
Chris@148 612
Chris@157 613 void
Chris@290 614 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 615 {
Chris@157 616 extensions.insert("mp3");
Chris@157 617 }
Chris@157 618
Chris@316 619 bool
Chris@316 620 MP3FileReader::supportsExtension(QString extension)
Chris@316 621 {
Chris@316 622 std::set<QString> extensions;
Chris@316 623 getSupportedExtensions(extensions);
Chris@316 624 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 625 }
Chris@316 626
Chris@316 627 bool
Chris@316 628 MP3FileReader::supportsContentType(QString type)
Chris@316 629 {
Chris@316 630 return (type == "audio/mpeg");
Chris@316 631 }
Chris@316 632
Chris@316 633 bool
Chris@317 634 MP3FileReader::supports(FileSource &source)
Chris@316 635 {
Chris@316 636 return (supportsExtension(source.getExtension()) ||
Chris@316 637 supportsContentType(source.getContentType()));
Chris@316 638 }
Chris@316 639
Chris@316 640
Chris@148 641 #endif