annotate data/fileio/MP3FileReader.cpp @ 1290:fa574c909c3d 3.0-integration

Add MAD_BUFFER_GUARD padding at end of mp3 buffer, in order to ensure last frame is decoded successfully (otherwise the decoded audio is truncated). Another thing learned from madplay.
author Chris Cannam
date Thu, 24 Nov 2016 17:06:31 +0000
parents a45312bd9306
children 9f9f55a8af92
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@186 35 #include <QFileInfo>
Chris@148 36
Chris@1218 37 #ifdef _MSC_VER
Chris@1218 38 #include <io.h>
Chris@1218 39 #define open _open
Chris@1218 40 #endif
Chris@1218 41
Chris@317 42 MP3FileReader::MP3FileReader(FileSource source, DecodeMode decodeMode,
Chris@1040 43 CacheMode mode, sv_samplerate_t targetRate,
Chris@920 44 bool normalised,
Chris@392 45 ProgressReporter *reporter) :
Chris@920 46 CodedAudioFileReader(mode, targetRate, normalised),
Chris@316 47 m_source(source),
Chris@316 48 m_path(source.getLocalFilename()),
Chris@1284 49 m_decodeErrorShown(false),
Chris@264 50 m_decodeThread(0)
Chris@148 51 {
Chris@1279 52 SVDEBUG << "MP3FileReader: local path: \"" << m_path
Chris@1279 53 << "\", decode mode: " << decodeMode << " ("
Chris@1279 54 << (decodeMode == DecodeAtOnce ? "DecodeAtOnce" : "DecodeThreaded")
Chris@1279 55 << ")" << endl;
Chris@1279 56
Chris@148 57 m_channelCount = 0;
Chris@297 58 m_fileRate = 0;
Chris@148 59 m_fileSize = 0;
Chris@148 60 m_bitrateNum = 0;
Chris@148 61 m_bitrateDenom = 0;
Chris@148 62 m_cancelled = false;
Chris@1288 63 m_mp3FrameCount = 0;
Chris@265 64 m_completion = 0;
Chris@263 65 m_done = false;
Chris@392 66 m_reporter = reporter;
Chris@148 67
Chris@148 68 struct stat stat;
Chris@316 69 if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
Chris@316 70 m_error = QString("File %1 does not exist.").arg(m_path);
Chris@148 71 return;
Chris@148 72 }
Chris@148 73
Chris@148 74 m_fileSize = stat.st_size;
Chris@148 75
Chris@1290 76 m_fileBuffer = 0;
Chris@1290 77 m_fileBufferSize = 0;
Chris@1290 78
Chris@1290 79 m_sampleBuffer = 0;
Chris@1290 80 m_sampleBufferSize = 0;
Chris@977 81
Chris@229 82 int fd = -1;
Chris@316 83 if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY
Chris@231 84 #ifdef _WIN32
Chris@231 85 | O_BINARY
Chris@231 86 #endif
Chris@231 87 , 0)) < 0) {
Chris@316 88 m_error = QString("Failed to open file %1 for reading.").arg(m_path);
Chris@148 89 return;
Chris@148 90 }
Chris@148 91
Chris@148 92 try {
Chris@1290 93 // We need a mysterious MAD_BUFFER_GUARD (== 8) zero bytes at
Chris@1290 94 // end of input, to ensure libmad decodes the last frame
Chris@1290 95 // correctly. Otherwise the decoded audio is truncated.
Chris@1290 96 m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD;
Chris@1290 97 m_fileBuffer = new unsigned char[m_fileBufferSize];
Chris@1290 98 memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD);
Chris@148 99 } catch (...) {
Chris@290 100 m_error = QString("Out of memory");
Chris@148 101 ::close(fd);
Chris@148 102 return;
Chris@148 103 }
Chris@148 104
Chris@229 105 ssize_t sz = 0;
Chris@1038 106 ssize_t offset = 0;
Chris@229 107 while (offset < m_fileSize) {
Chris@1290 108 sz = ::read(fd, m_fileBuffer + offset, m_fileSize - offset);
Chris@229 109 if (sz < 0) {
Chris@290 110 m_error = QString("Read error for file %1 (after %2 bytes)")
Chris@316 111 .arg(m_path).arg(offset);
Chris@1290 112 delete[] m_fileBuffer;
Chris@229 113 ::close(fd);
Chris@229 114 return;
Chris@230 115 } else if (sz == 0) {
Chris@1290 116 SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
Chris@843 117 .arg(offset).arg(m_fileSize) << endl;
Chris@231 118 m_fileSize = offset;
Chris@1290 119 m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD;
Chris@1290 120 memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD);
Chris@230 121 break;
Chris@229 122 }
Chris@229 123 offset += sz;
Chris@148 124 }
Chris@148 125
Chris@148 126 ::close(fd);
Chris@148 127
Chris@271 128 loadTags();
Chris@271 129
Chris@263 130 if (decodeMode == DecodeAtOnce) {
Chris@263 131
Chris@392 132 if (m_reporter) {
Chris@392 133 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
Chris@392 134 m_reporter->setMessage
Chris@392 135 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
Chris@327 136 }
Chris@148 137
Chris@1290 138 if (!decode(m_fileBuffer, m_fileBufferSize)) {
Chris@316 139 m_error = QString("Failed to decode file %1.").arg(m_path);
Chris@263 140 }
Chris@263 141
Chris@1290 142 delete[] m_fileBuffer;
Chris@1290 143 m_fileBuffer = 0;
Chris@148 144
Chris@263 145 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@398 146 endSerialised();
Chris@263 147
Chris@392 148 } else {
Chris@263 149
Chris@392 150 if (m_reporter) m_reporter->setProgress(100);
Chris@263 151
Chris@263 152 m_decodeThread = new DecodeThread(this);
Chris@263 153 m_decodeThread->start();
Chris@263 154
Chris@386 155 while ((m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0)
Chris@386 156 && !m_done) {
Chris@263 157 usleep(10);
Chris@263 158 }
Chris@386 159
Chris@1288 160 SVDEBUG << "MP3FileReader: decoding startup complete, file rate = " << m_fileRate << endl;
Chris@148 161 }
Chris@499 162
Chris@499 163 if (m_error != "") {
Chris@1279 164 SVDEBUG << "MP3FileReader::MP3FileReader(\"" << m_path << "\"): ERROR: " << m_error << endl;
Chris@499 165 }
Chris@148 166 }
Chris@148 167
Chris@148 168 MP3FileReader::~MP3FileReader()
Chris@148 169 {
Chris@263 170 if (m_decodeThread) {
Chris@265 171 m_cancelled = true;
Chris@263 172 m_decodeThread->wait();
Chris@263 173 delete m_decodeThread;
Chris@263 174 }
Chris@148 175 }
Chris@148 176
Chris@263 177 void
Chris@392 178 MP3FileReader::cancelled()
Chris@392 179 {
Chris@392 180 m_cancelled = true;
Chris@392 181 }
Chris@392 182
Chris@392 183 void
Chris@271 184 MP3FileReader::loadTags()
Chris@271 185 {
Chris@271 186 m_title = "";
Chris@271 187
Chris@271 188 #ifdef HAVE_ID3TAG
Chris@271 189
Chris@290 190 id3_file *file = id3_file_open(m_path.toLocal8Bit().data(),
Chris@271 191 ID3_FILE_MODE_READONLY);
Chris@271 192 if (!file) return;
Chris@271 193
Chris@273 194 // We can do this a lot more elegantly, but we'll leave that for
Chris@273 195 // when we implement support for more than just the one tag!
Chris@273 196
Chris@271 197 id3_tag *tag = id3_file_tag(file);
Chris@273 198 if (!tag) {
Chris@1279 199 SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl;
Chris@273 200 id3_file_close(file);
Chris@273 201 return;
Chris@271 202 }
Chris@271 203
Chris@333 204 m_title = loadTag(tag, "TIT2"); // work title
Chris@333 205 if (m_title == "") m_title = loadTag(tag, "TIT1");
Chris@1287 206 if (m_title == "") SVDEBUG << "MP3FileReader::loadTags: No title found" << endl;
Chris@273 207
Chris@333 208 m_maker = loadTag(tag, "TPE1"); // "lead artist"
Chris@333 209 if (m_maker == "") m_maker = loadTag(tag, "TPE2");
Chris@1287 210 if (m_maker == "") SVDEBUG << "MP3FileReader::loadTags: No artist/maker found" << endl;
Chris@273 211
Chris@632 212 for (unsigned int i = 0; i < tag->nframes; ++i) {
Chris@632 213 if (tag->frames[i]) {
Chris@632 214 QString value = loadTag(tag, tag->frames[i]->id);
Chris@1287 215 if (value != "") {
Chris@1287 216 m_tags[tag->frames[i]->id] = value;
Chris@1287 217 }
Chris@632 218 }
Chris@632 219 }
Chris@632 220
Chris@271 221 id3_file_close(file);
Chris@273 222
Chris@273 223 #else
Chris@1287 224 SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in" << endl;
Chris@273 225 #endif
Chris@333 226 }
Chris@273 227
Chris@333 228 QString
Chris@333 229 MP3FileReader::loadTag(void *vtag, const char *name)
Chris@333 230 {
Chris@333 231 #ifdef HAVE_ID3TAG
Chris@333 232 id3_tag *tag = (id3_tag *)vtag;
Chris@333 233
Chris@333 234 id3_frame *frame = id3_tag_findframe(tag, name, 0);
Chris@333 235 if (!frame) {
Chris@1287 236 SVDEBUG << "MP3FileReader::loadTag: No \"" << name << "\" frame found in ID3 tag" << endl;
Chris@333 237 return "";
Chris@333 238 }
Chris@333 239
Chris@333 240 if (frame->nfields < 2) {
Chris@1287 241 cerr << "MP3FileReader::loadTag: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << endl;
Chris@333 242 return "";
Chris@333 243 }
Chris@333 244
Chris@333 245 unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
Chris@333 246 if (nstrings == 0) {
Chris@1287 247 SVDEBUG << "MP3FileReader::loadTag: No strings for \"" << name << "\" in ID3 tag" << endl;
Chris@333 248 return "";
Chris@333 249 }
Chris@333 250
Chris@333 251 id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
Chris@333 252 if (!ustr) {
Chris@1287 253 SVDEBUG << "MP3FileReader::loadTag: Invalid or absent data for \"" << name << "\" in ID3 tag" << endl;
Chris@333 254 return "";
Chris@333 255 }
Chris@333 256
Chris@333 257 id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
Chris@333 258 if (!u8str) {
Chris@1287 259 SVDEBUG << "MP3FileReader::loadTag: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 tag" << endl;
Chris@333 260 return "";
Chris@333 261 }
Chris@333 262
Chris@333 263 QString rv = QString::fromUtf8((const char *)u8str);
Chris@333 264 free(u8str);
Chris@334 265
Chris@1287 266 SVDEBUG << "MP3FileReader::loadTag: Tag \"" << name << "\" -> \""
Chris@1287 267 << rv << "\"" << endl;
Chris@334 268
Chris@333 269 return rv;
Chris@333 270
Chris@333 271 #else
Chris@333 272 return "";
Chris@333 273 #endif
Chris@271 274 }
Chris@271 275
Chris@271 276 void
Chris@263 277 MP3FileReader::DecodeThread::run()
Chris@263 278 {
Chris@1290 279 if (!m_reader->decode(m_reader->m_fileBuffer, m_reader->m_fileBufferSize)) {
Chris@290 280 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path);
Chris@263 281 }
Chris@263 282
Chris@1290 283 delete[] m_reader->m_fileBuffer;
Chris@1290 284 m_reader->m_fileBuffer = 0;
Chris@297 285
Chris@1290 286 if (m_reader->m_sampleBuffer) {
Chris@929 287 for (int c = 0; c < m_reader->m_channelCount; ++c) {
Chris@1290 288 delete[] m_reader->m_sampleBuffer[c];
Chris@297 289 }
Chris@1290 290 delete[] m_reader->m_sampleBuffer;
Chris@1290 291 m_reader->m_sampleBuffer = 0;
Chris@297 292 }
Chris@297 293
Chris@263 294 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@263 295
Chris@263 296 m_reader->m_done = true;
Chris@265 297 m_reader->m_completion = 100;
Chris@297 298
Chris@297 299 m_reader->endSerialised();
Chris@263 300 }
Chris@263 301
Chris@148 302 bool
Chris@1038 303 MP3FileReader::decode(void *mm, sv_frame_t sz)
Chris@148 304 {
Chris@148 305 DecoderData data;
Chris@148 306 struct mad_decoder decoder;
Chris@148 307
Chris@148 308 data.start = (unsigned char const *)mm;
Chris@1288 309 data.length = sz;
Chris@148 310 data.reader = this;
Chris@148 311
Chris@1288 312 mad_decoder_init(&decoder, // decoder to initialise
Chris@1288 313 &data, // our own data block for callbacks
Chris@1288 314 input_callback, // provides (entire) input to mad
Chris@1288 315 0, // checks header
Chris@1288 316 filter_callback, // filters frame before decoding
Chris@1288 317 output_callback, // receives decoded output
Chris@1288 318 error_callback, // handles decode errors
Chris@1288 319 0); // "message_func"
Chris@1288 320
Chris@148 321 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
Chris@148 322 mad_decoder_finish(&decoder);
Chris@148 323
Chris@1288 324 SVDEBUG << "MP3FileReader: Decoding complete, decoded " << m_mp3FrameCount
Chris@1288 325 << " mp3 frames" << endl;
Chris@1288 326
Chris@263 327 m_done = true;
Chris@148 328 return true;
Chris@148 329 }
Chris@148 330
Chris@148 331 enum mad_flow
Chris@1288 332 MP3FileReader::input_callback(void *dp, struct mad_stream *stream)
Chris@148 333 {
Chris@148 334 DecoderData *data = (DecoderData *)dp;
Chris@148 335
Chris@148 336 if (!data->length) return MAD_FLOW_STOP;
Chris@348 337
Chris@348 338 unsigned char const *start = data->start;
Chris@1288 339 sv_frame_t length = data->length;
Chris@348 340
Chris@348 341 #ifdef HAVE_ID3TAG
Chris@1288 342 while (length > ID3_TAG_QUERYSIZE) {
Chris@1038 343 ssize_t taglen = id3_tag_query(start, ID3_TAG_QUERYSIZE);
Chris@1288 344 if (taglen <= 0) {
Chris@1288 345 break;
Chris@348 346 }
Chris@1288 347 SVDEBUG << "MP3FileReader: ID3 tag length to skip: " << taglen << endl;
Chris@1288 348 start += taglen;
Chris@1288 349 length -= taglen;
Chris@348 350 }
Chris@348 351 #endif
Chris@348 352
Chris@348 353 mad_stream_buffer(stream, start, length);
Chris@148 354 data->length = 0;
Chris@148 355
Chris@148 356 return MAD_FLOW_CONTINUE;
Chris@148 357 }
Chris@148 358
Chris@148 359 enum mad_flow
Chris@1288 360 MP3FileReader::filter_callback(void *dp,
Chris@1288 361 struct mad_stream const *stream,
Chris@1288 362 struct mad_frame *frame)
Chris@1288 363 {
Chris@1288 364 DecoderData *data = (DecoderData *)dp;
Chris@1288 365 return data->reader->filter(stream, frame);
Chris@1288 366 }
Chris@1288 367
Chris@1288 368 enum mad_flow
Chris@1288 369 MP3FileReader::filter(struct mad_stream const *stream,
Chris@1288 370 struct mad_frame *)
Chris@1288 371 {
Chris@1289 372 if (m_mp3FrameCount > 0) {
Chris@1289 373 return MAD_FLOW_CONTINUE;
Chris@1288 374 } else {
Chris@1289 375 struct mad_bitptr ptr = stream->anc_ptr;
Chris@1289 376 unsigned long fourcc = mad_bit_read(&ptr, 32);
Chris@1289 377 std::string magic("....");
Chris@1289 378 for (int i = 0; i < 4; ++i) {
Chris@1289 379 magic[3-i] = char((fourcc >> (8*i)) & 0xff);
Chris@1289 380 }
Chris@1289 381 if (magic == "Xing" || magic == "Info" || magic == "LAME") {
Chris@1289 382 SVDEBUG << "MP3FileReader: Discarding metadata frame (magic = \""
Chris@1289 383 << magic << "\")" << endl;
Chris@1289 384 return MAD_FLOW_IGNORE;
Chris@1289 385 } else {
Chris@1289 386 return MAD_FLOW_CONTINUE;
Chris@1289 387 }
Chris@1288 388 }
Chris@1288 389 }
Chris@1288 390
Chris@1288 391 enum mad_flow
Chris@1288 392 MP3FileReader::output_callback(void *dp,
Chris@1288 393 struct mad_header const *header,
Chris@1288 394 struct mad_pcm *pcm)
Chris@148 395 {
Chris@148 396 DecoderData *data = (DecoderData *)dp;
Chris@148 397 return data->reader->accept(header, pcm);
Chris@148 398 }
Chris@148 399
Chris@148 400 enum mad_flow
Chris@148 401 MP3FileReader::accept(struct mad_header const *header,
Chris@148 402 struct mad_pcm *pcm)
Chris@148 403 {
Chris@148 404 int channels = pcm->channels;
Chris@148 405 int frames = pcm->length;
Chris@1288 406
Chris@148 407 if (header) {
Chris@1038 408 m_bitrateNum = m_bitrateNum + double(header->bitrate);
Chris@148 409 m_bitrateDenom ++;
Chris@148 410 }
Chris@148 411
Chris@148 412 if (frames < 1) return MAD_FLOW_CONTINUE;
Chris@148 413
Chris@148 414 if (m_channelCount == 0) {
Chris@297 415
Chris@297 416 m_fileRate = pcm->samplerate;
Chris@148 417 m_channelCount = channels;
Chris@297 418
Chris@297 419 initialiseDecodeCache();
Chris@297 420
Chris@297 421 if (m_cacheMode == CacheInTemporaryFile) {
Chris@690 422 // SVDEBUG << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << endl;
Chris@297 423 startSerialised("MP3FileReader::Decode");
Chris@297 424 }
Chris@148 425 }
Chris@148 426
Chris@148 427 if (m_bitrateDenom > 0) {
Chris@148 428 double bitrate = m_bitrateNum / m_bitrateDenom;
Chris@148 429 double duration = double(m_fileSize * 8) / bitrate;
Chris@148 430 double elapsed = double(m_frameCount) / m_sampleRate;
Chris@375 431 double percent = 100;
Chris@375 432 if (duration > 0.0) percent = ((elapsed * 100.0) / duration);
Chris@357 433 int p = int(percent);
Chris@357 434 if (p < 1) p = 1;
Chris@357 435 if (p > 99) p = 99;
Chris@392 436 if (m_completion != p && m_reporter) {
Chris@357 437 m_completion = p;
Chris@392 438 m_reporter->setProgress(m_completion);
Chris@148 439 }
Chris@148 440 }
Chris@148 441
Chris@1290 442 if (m_cancelled) {
Chris@1290 443 SVDEBUG << "MP3FileReader: Decoding cancelled" << endl;
Chris@1290 444 return MAD_FLOW_STOP;
Chris@1290 445 }
Chris@148 446
Chris@148 447 if (!isDecodeCacheInitialised()) {
Chris@148 448 initialiseDecodeCache();
Chris@148 449 }
Chris@148 450
Chris@1290 451 if (m_sampleBufferSize < size_t(frames)) {
Chris@1290 452 if (!m_sampleBuffer) {
Chris@1290 453 m_sampleBuffer = new float *[channels];
Chris@297 454 for (int c = 0; c < channels; ++c) {
Chris@1290 455 m_sampleBuffer[c] = 0;
Chris@297 456 }
Chris@297 457 }
Chris@297 458 for (int c = 0; c < channels; ++c) {
Chris@1290 459 delete[] m_sampleBuffer[c];
Chris@1290 460 m_sampleBuffer[c] = new float[frames];
Chris@297 461 }
Chris@1290 462 m_sampleBufferSize = frames;
Chris@297 463 }
Chris@148 464
Chris@297 465 int activeChannels = int(sizeof(pcm->samples) / sizeof(pcm->samples[0]));
Chris@297 466
Chris@297 467 for (int ch = 0; ch < channels; ++ch) {
Chris@297 468
Chris@297 469 for (int i = 0; i < frames; ++i) {
Chris@297 470
Chris@148 471 mad_fixed_t sample = 0;
Chris@297 472 if (ch < activeChannels) {
Chris@148 473 sample = pcm->samples[ch][i];
Chris@148 474 }
Chris@148 475 float fsample = float(sample) / float(MAD_F_ONE);
Chris@297 476
Chris@1290 477 m_sampleBuffer[ch][i] = fsample;
Chris@148 478 }
Chris@148 479 }
Chris@148 480
Chris@1290 481 addSamplesToDecodeCache(m_sampleBuffer, frames);
Chris@148 482
Chris@1288 483 ++m_mp3FrameCount;
Chris@1288 484
Chris@148 485 return MAD_FLOW_CONTINUE;
Chris@148 486 }
Chris@148 487
Chris@148 488 enum mad_flow
Chris@1288 489 MP3FileReader::error_callback(void *dp,
Chris@1288 490 struct mad_stream *stream,
Chris@1288 491 struct mad_frame *)
Chris@148 492 {
Chris@1284 493 DecoderData *data = (DecoderData *)dp;
Chris@1290 494
Chris@1290 495 if (stream->error == MAD_ERROR_LOSTSYNC &&
Chris@1290 496 data->length == 0) {
Chris@1290 497 // We are at end of file, losing sync is expected behaviour
Chris@1290 498 return MAD_FLOW_CONTINUE;
Chris@1290 499 }
Chris@1290 500
Chris@1284 501 if (!data->reader->m_decodeErrorShown) {
Chris@1284 502 char buffer[256];
Chris@1284 503 snprintf(buffer, 255,
Chris@1284 504 "MP3 decoding error 0x%04x (%s) at byte offset %lu",
Chris@1284 505 stream->error, mad_stream_errorstr(stream),
Chris@1284 506 (unsigned long)(stream->this_frame - data->start));
Chris@1284 507 SVCERR << "Warning: in file \"" << data->reader->m_path << "\": "
Chris@1284 508 << buffer << " (continuing; will not report any further decode errors for this file)" << endl;
Chris@1284 509 data->reader->m_decodeErrorShown = true;
Chris@1279 510 }
Chris@148 511
Chris@148 512 return MAD_FLOW_CONTINUE;
Chris@148 513 }
Chris@148 514
Chris@157 515 void
Chris@290 516 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 517 {
Chris@157 518 extensions.insert("mp3");
Chris@157 519 }
Chris@157 520
Chris@316 521 bool
Chris@316 522 MP3FileReader::supportsExtension(QString extension)
Chris@316 523 {
Chris@316 524 std::set<QString> extensions;
Chris@316 525 getSupportedExtensions(extensions);
Chris@316 526 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 527 }
Chris@316 528
Chris@316 529 bool
Chris@316 530 MP3FileReader::supportsContentType(QString type)
Chris@316 531 {
Chris@316 532 return (type == "audio/mpeg");
Chris@316 533 }
Chris@316 534
Chris@316 535 bool
Chris@317 536 MP3FileReader::supports(FileSource &source)
Chris@316 537 {
Chris@316 538 return (supportsExtension(source.getExtension()) ||
Chris@316 539 supportsContentType(source.getContentType()));
Chris@316 540 }
Chris@316 541
Chris@316 542
Chris@148 543 #endif