annotate data/fileio/MP3FileReader.cpp @ 1376:d9511f9e04d7 dev/refactor-piper-related

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