annotate data/fileio/MP3FileReader.cpp @ 1346:75ad55315db4 3.0-integration

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