annotate data/fileio/MP3FileReader.cpp @ 1305:9f9f55a8af92 mp3-gapless

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