annotate data/fileio/MP3FileReader.cpp @ 1078:ce82bcdc95d0

Fail upfront if the file is going to be too large. We expect the caller to split up large data sets into several MatrixFiles
author Chris Cannam
date Wed, 10 Jun 2015 13:10:26 +0100
parents a1cd5abcb38b
children 52d088c24c5c
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@610 30 #include <unistd.h>
Chris@405 31
Chris@271 32 #ifdef HAVE_ID3TAG
Chris@271 33 #include <id3tag.h>
Chris@271 34 #endif
Chris@510 35 //#define DEBUG_ID3TAG 1
Chris@271 36
Chris@186 37 #include <QFileInfo>
Chris@148 38
Chris@317 39 MP3FileReader::MP3FileReader(FileSource source, DecodeMode decodeMode,
Chris@1040 40 CacheMode mode, sv_samplerate_t targetRate,
Chris@920 41 bool normalised,
Chris@392 42 ProgressReporter *reporter) :
Chris@920 43 CodedAudioFileReader(mode, targetRate, normalised),
Chris@316 44 m_source(source),
Chris@316 45 m_path(source.getLocalFilename()),
Chris@264 46 m_decodeThread(0)
Chris@148 47 {
Chris@148 48 m_channelCount = 0;
Chris@297 49 m_fileRate = 0;
Chris@148 50 m_fileSize = 0;
Chris@148 51 m_bitrateNum = 0;
Chris@148 52 m_bitrateDenom = 0;
Chris@148 53 m_cancelled = false;
Chris@265 54 m_completion = 0;
Chris@263 55 m_done = false;
Chris@392 56 m_reporter = reporter;
Chris@148 57
Chris@148 58 struct stat stat;
Chris@316 59 if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
Chris@316 60 m_error = QString("File %1 does not exist.").arg(m_path);
Chris@148 61 return;
Chris@148 62 }
Chris@148 63
Chris@148 64 m_fileSize = stat.st_size;
Chris@148 65
Chris@977 66 m_filebuffer = 0;
Chris@977 67 m_samplebuffer = 0;
Chris@977 68 m_samplebuffersize = 0;
Chris@977 69
Chris@229 70 int fd = -1;
Chris@316 71 if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY
Chris@231 72 #ifdef _WIN32
Chris@231 73 | O_BINARY
Chris@231 74 #endif
Chris@231 75 , 0)) < 0) {
Chris@316 76 m_error = QString("Failed to open file %1 for reading.").arg(m_path);
Chris@148 77 return;
Chris@148 78 }
Chris@148 79
Chris@148 80 try {
Chris@263 81 m_filebuffer = new unsigned char[m_fileSize];
Chris@148 82 } catch (...) {
Chris@290 83 m_error = QString("Out of memory");
Chris@148 84 ::close(fd);
Chris@148 85 return;
Chris@148 86 }
Chris@148 87
Chris@229 88 ssize_t sz = 0;
Chris@1038 89 ssize_t offset = 0;
Chris@229 90 while (offset < m_fileSize) {
Chris@263 91 sz = ::read(fd, m_filebuffer + offset, m_fileSize - offset);
Chris@229 92 if (sz < 0) {
Chris@290 93 m_error = QString("Read error for file %1 (after %2 bytes)")
Chris@316 94 .arg(m_path).arg(offset);
Chris@263 95 delete[] m_filebuffer;
Chris@229 96 ::close(fd);
Chris@229 97 return;
Chris@230 98 } else if (sz == 0) {
Chris@843 99 cerr << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
Chris@843 100 .arg(offset).arg(m_fileSize) << endl;
Chris@231 101 m_fileSize = offset;
Chris@230 102 break;
Chris@229 103 }
Chris@229 104 offset += sz;
Chris@148 105 }
Chris@148 106
Chris@148 107 ::close(fd);
Chris@148 108
Chris@271 109 loadTags();
Chris@271 110
Chris@263 111 if (decodeMode == DecodeAtOnce) {
Chris@263 112
Chris@392 113 if (m_reporter) {
Chris@392 114 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
Chris@392 115 m_reporter->setMessage
Chris@392 116 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
Chris@327 117 }
Chris@148 118
Chris@263 119 if (!decode(m_filebuffer, m_fileSize)) {
Chris@316 120 m_error = QString("Failed to decode file %1.").arg(m_path);
Chris@263 121 }
Chris@263 122
Chris@263 123 delete[] m_filebuffer;
Chris@263 124 m_filebuffer = 0;
Chris@148 125
Chris@263 126 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@398 127 endSerialised();
Chris@263 128
Chris@392 129 } else {
Chris@263 130
Chris@392 131 if (m_reporter) m_reporter->setProgress(100);
Chris@263 132
Chris@263 133 m_decodeThread = new DecodeThread(this);
Chris@263 134 m_decodeThread->start();
Chris@263 135
Chris@386 136 while ((m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0)
Chris@386 137 && !m_done) {
Chris@263 138 usleep(10);
Chris@263 139 }
Chris@386 140
Chris@843 141 cerr << "MP3FileReader ctor: exiting with file rate = " << m_fileRate << endl;
Chris@148 142 }
Chris@499 143
Chris@499 144 if (m_error != "") {
Chris@843 145 cerr << "MP3FileReader::MP3FileReader(\"" << m_path << "\"): ERROR: " << m_error << endl;
Chris@499 146 }
Chris@148 147 }
Chris@148 148
Chris@148 149 MP3FileReader::~MP3FileReader()
Chris@148 150 {
Chris@263 151 if (m_decodeThread) {
Chris@265 152 m_cancelled = true;
Chris@263 153 m_decodeThread->wait();
Chris@263 154 delete m_decodeThread;
Chris@263 155 }
Chris@148 156 }
Chris@148 157
Chris@263 158 void
Chris@392 159 MP3FileReader::cancelled()
Chris@392 160 {
Chris@392 161 m_cancelled = true;
Chris@392 162 }
Chris@392 163
Chris@392 164 void
Chris@271 165 MP3FileReader::loadTags()
Chris@271 166 {
Chris@271 167 m_title = "";
Chris@271 168
Chris@271 169 #ifdef HAVE_ID3TAG
Chris@271 170
Chris@290 171 id3_file *file = id3_file_open(m_path.toLocal8Bit().data(),
Chris@271 172 ID3_FILE_MODE_READONLY);
Chris@271 173 if (!file) return;
Chris@271 174
Chris@273 175 // We can do this a lot more elegantly, but we'll leave that for
Chris@273 176 // when we implement support for more than just the one tag!
Chris@273 177
Chris@271 178 id3_tag *tag = id3_file_tag(file);
Chris@273 179 if (!tag) {
Chris@273 180 #ifdef DEBUG_ID3TAG
Chris@690 181 SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl;
Chris@273 182 #endif
Chris@273 183 id3_file_close(file);
Chris@273 184 return;
Chris@271 185 }
Chris@271 186
Chris@333 187 m_title = loadTag(tag, "TIT2"); // work title
Chris@333 188 if (m_title == "") m_title = loadTag(tag, "TIT1");
Chris@273 189
Chris@333 190 m_maker = loadTag(tag, "TPE1"); // "lead artist"
Chris@333 191 if (m_maker == "") m_maker = loadTag(tag, "TPE2");
Chris@273 192
Chris@632 193 for (unsigned int i = 0; i < tag->nframes; ++i) {
Chris@632 194 if (tag->frames[i]) {
Chris@632 195 QString value = loadTag(tag, tag->frames[i]->id);
Chris@632 196 if (value != "") m_tags[tag->frames[i]->id] = value;
Chris@632 197 }
Chris@632 198 }
Chris@632 199
Chris@271 200 id3_file_close(file);
Chris@273 201
Chris@273 202 #else
Chris@273 203 #ifdef DEBUG_ID3TAG
Chris@690 204 SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in"
Chris@687 205 << endl;
Chris@271 206 #endif
Chris@273 207 #endif
Chris@333 208 }
Chris@273 209
Chris@333 210 QString
Chris@333 211 MP3FileReader::loadTag(void *vtag, const char *name)
Chris@333 212 {
Chris@333 213 #ifdef HAVE_ID3TAG
Chris@333 214 id3_tag *tag = (id3_tag *)vtag;
Chris@333 215
Chris@333 216 id3_frame *frame = id3_tag_findframe(tag, name, 0);
Chris@333 217 if (!frame) {
Chris@333 218 #ifdef DEBUG_ID3TAG
Chris@690 219 SVDEBUG << "MP3FileReader::loadTags: No \"" << name << "\" in ID3 tag" << endl;
Chris@333 220 #endif
Chris@333 221 return "";
Chris@333 222 }
Chris@333 223
Chris@333 224 if (frame->nfields < 2) {
Chris@690 225 SVDEBUG << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << endl;
Chris@333 226 return "";
Chris@333 227 }
Chris@333 228
Chris@333 229 unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
Chris@333 230 if (nstrings == 0) {
Chris@333 231 #ifdef DEBUG_ID3TAG
Chris@690 232 SVDEBUG << "MP3FileReader::loadTags: No strings for \"" << name << "\" in ID3 tag" << endl;
Chris@333 233 #endif
Chris@333 234 return "";
Chris@333 235 }
Chris@333 236
Chris@333 237 id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
Chris@333 238 if (!ustr) {
Chris@333 239 #ifdef DEBUG_ID3TAG
Chris@690 240 SVDEBUG << "MP3FileReader::loadTags: Invalid or absent data for \"" << name << "\" in ID3 tag" << endl;
Chris@333 241 #endif
Chris@333 242 return "";
Chris@333 243 }
Chris@333 244
Chris@333 245 id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
Chris@333 246 if (!u8str) {
Chris@843 247 cerr << "MP3FileReader::loadTags: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 title" << endl;
Chris@333 248 return "";
Chris@333 249 }
Chris@333 250
Chris@333 251 QString rv = QString::fromUtf8((const char *)u8str);
Chris@333 252 free(u8str);
Chris@334 253
Chris@334 254 #ifdef DEBUG_ID3TAG
Chris@690 255 SVDEBUG << "MP3FileReader::loadTags: tag \"" << name << "\" -> \""
Chris@687 256 << rv << "\"" << endl;
Chris@334 257 #endif
Chris@334 258
Chris@334 259
Chris@333 260 return rv;
Chris@333 261
Chris@333 262 #else
Chris@333 263 return "";
Chris@333 264 #endif
Chris@271 265 }
Chris@271 266
Chris@271 267 void
Chris@263 268 MP3FileReader::DecodeThread::run()
Chris@263 269 {
Chris@263 270 if (!m_reader->decode(m_reader->m_filebuffer, m_reader->m_fileSize)) {
Chris@290 271 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path);
Chris@263 272 }
Chris@263 273
Chris@263 274 delete[] m_reader->m_filebuffer;
Chris@263 275 m_reader->m_filebuffer = 0;
Chris@297 276
Chris@297 277 if (m_reader->m_samplebuffer) {
Chris@929 278 for (int c = 0; c < m_reader->m_channelCount; ++c) {
Chris@297 279 delete[] m_reader->m_samplebuffer[c];
Chris@297 280 }
Chris@297 281 delete[] m_reader->m_samplebuffer;
Chris@297 282 m_reader->m_samplebuffer = 0;
Chris@297 283 }
Chris@297 284
Chris@263 285 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@263 286
Chris@263 287 m_reader->m_done = true;
Chris@265 288 m_reader->m_completion = 100;
Chris@297 289
Chris@297 290 m_reader->endSerialised();
Chris@263 291 }
Chris@263 292
Chris@148 293 bool
Chris@1038 294 MP3FileReader::decode(void *mm, sv_frame_t sz)
Chris@148 295 {
Chris@148 296 DecoderData data;
Chris@148 297 struct mad_decoder decoder;
Chris@148 298
Chris@148 299 data.start = (unsigned char const *)mm;
Chris@148 300 data.length = (unsigned long)sz;
Chris@148 301 data.reader = this;
Chris@148 302
Chris@148 303 mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0);
Chris@148 304 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
Chris@148 305 mad_decoder_finish(&decoder);
Chris@148 306
Chris@263 307 m_done = true;
Chris@148 308 return true;
Chris@148 309 }
Chris@148 310
Chris@148 311 enum mad_flow
Chris@148 312 MP3FileReader::input(void *dp, struct mad_stream *stream)
Chris@148 313 {
Chris@148 314 DecoderData *data = (DecoderData *)dp;
Chris@148 315
Chris@148 316 if (!data->length) return MAD_FLOW_STOP;
Chris@348 317
Chris@348 318 unsigned char const *start = data->start;
Chris@348 319 unsigned long length = data->length;
Chris@348 320
Chris@348 321 #ifdef HAVE_ID3TAG
Chris@348 322 if (length > ID3_TAG_QUERYSIZE) {
Chris@1038 323 ssize_t taglen = id3_tag_query(start, ID3_TAG_QUERYSIZE);
Chris@348 324 if (taglen > 0) {
Chris@843 325 // cerr << "ID3 tag length to skip: " << taglen << endl;
Chris@348 326 start += taglen;
Chris@348 327 length -= taglen;
Chris@348 328 }
Chris@348 329 }
Chris@348 330 #endif
Chris@348 331
Chris@348 332 mad_stream_buffer(stream, start, length);
Chris@148 333 data->length = 0;
Chris@148 334
Chris@148 335 return MAD_FLOW_CONTINUE;
Chris@148 336 }
Chris@148 337
Chris@148 338 enum mad_flow
Chris@148 339 MP3FileReader::output(void *dp,
Chris@148 340 struct mad_header const *header,
Chris@148 341 struct mad_pcm *pcm)
Chris@148 342 {
Chris@148 343 DecoderData *data = (DecoderData *)dp;
Chris@148 344 return data->reader->accept(header, pcm);
Chris@148 345 }
Chris@148 346
Chris@148 347 enum mad_flow
Chris@148 348 MP3FileReader::accept(struct mad_header const *header,
Chris@148 349 struct mad_pcm *pcm)
Chris@148 350 {
Chris@148 351 int channels = pcm->channels;
Chris@148 352 int frames = pcm->length;
Chris@148 353
Chris@148 354 if (header) {
Chris@1038 355 m_bitrateNum = m_bitrateNum + double(header->bitrate);
Chris@148 356 m_bitrateDenom ++;
Chris@148 357 }
Chris@148 358
Chris@148 359 if (frames < 1) return MAD_FLOW_CONTINUE;
Chris@148 360
Chris@148 361 if (m_channelCount == 0) {
Chris@297 362
Chris@297 363 m_fileRate = pcm->samplerate;
Chris@148 364 m_channelCount = channels;
Chris@297 365
Chris@297 366 initialiseDecodeCache();
Chris@297 367
Chris@297 368 if (m_cacheMode == CacheInTemporaryFile) {
Chris@690 369 // SVDEBUG << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << endl;
Chris@297 370 startSerialised("MP3FileReader::Decode");
Chris@297 371 }
Chris@148 372 }
Chris@148 373
Chris@148 374 if (m_bitrateDenom > 0) {
Chris@148 375 double bitrate = m_bitrateNum / m_bitrateDenom;
Chris@148 376 double duration = double(m_fileSize * 8) / bitrate;
Chris@148 377 double elapsed = double(m_frameCount) / m_sampleRate;
Chris@375 378 double percent = 100;
Chris@375 379 if (duration > 0.0) percent = ((elapsed * 100.0) / duration);
Chris@357 380 int p = int(percent);
Chris@357 381 if (p < 1) p = 1;
Chris@357 382 if (p > 99) p = 99;
Chris@392 383 if (m_completion != p && m_reporter) {
Chris@357 384 m_completion = p;
Chris@392 385 m_reporter->setProgress(m_completion);
Chris@148 386 }
Chris@148 387 }
Chris@148 388
Chris@148 389 if (m_cancelled) return MAD_FLOW_STOP;
Chris@148 390
Chris@148 391 if (!isDecodeCacheInitialised()) {
Chris@148 392 initialiseDecodeCache();
Chris@148 393 }
Chris@148 394
Chris@929 395 if (int(m_samplebuffersize) < frames) {
Chris@297 396 if (!m_samplebuffer) {
Chris@297 397 m_samplebuffer = new float *[channels];
Chris@297 398 for (int c = 0; c < channels; ++c) {
Chris@297 399 m_samplebuffer[c] = 0;
Chris@297 400 }
Chris@297 401 }
Chris@297 402 for (int c = 0; c < channels; ++c) {
Chris@297 403 delete[] m_samplebuffer[c];
Chris@297 404 m_samplebuffer[c] = new float[frames];
Chris@297 405 }
Chris@297 406 m_samplebuffersize = frames;
Chris@297 407 }
Chris@148 408
Chris@297 409 int activeChannels = int(sizeof(pcm->samples) / sizeof(pcm->samples[0]));
Chris@297 410
Chris@297 411 for (int ch = 0; ch < channels; ++ch) {
Chris@297 412
Chris@297 413 for (int i = 0; i < frames; ++i) {
Chris@297 414
Chris@148 415 mad_fixed_t sample = 0;
Chris@297 416 if (ch < activeChannels) {
Chris@148 417 sample = pcm->samples[ch][i];
Chris@148 418 }
Chris@148 419 float fsample = float(sample) / float(MAD_F_ONE);
Chris@297 420
Chris@297 421 m_samplebuffer[ch][i] = fsample;
Chris@148 422 }
Chris@148 423 }
Chris@148 424
Chris@297 425 addSamplesToDecodeCache(m_samplebuffer, frames);
Chris@148 426
Chris@148 427 return MAD_FLOW_CONTINUE;
Chris@148 428 }
Chris@148 429
Chris@148 430 enum mad_flow
Chris@929 431 MP3FileReader::error(void * /* dp */,
Chris@929 432 struct mad_stream * /* stream */,
Chris@148 433 struct mad_frame *)
Chris@148 434 {
Chris@929 435 // DecoderData *data = (DecoderData *)dp;
Chris@148 436
Chris@523 437 // fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %lu\n",
Chris@523 438 // stream->error, mad_stream_errorstr(stream),
Chris@523 439 // (unsigned long)(stream->this_frame - data->start));
Chris@148 440
Chris@148 441 return MAD_FLOW_CONTINUE;
Chris@148 442 }
Chris@148 443
Chris@157 444 void
Chris@290 445 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 446 {
Chris@157 447 extensions.insert("mp3");
Chris@157 448 }
Chris@157 449
Chris@316 450 bool
Chris@316 451 MP3FileReader::supportsExtension(QString extension)
Chris@316 452 {
Chris@316 453 std::set<QString> extensions;
Chris@316 454 getSupportedExtensions(extensions);
Chris@316 455 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 456 }
Chris@316 457
Chris@316 458 bool
Chris@316 459 MP3FileReader::supportsContentType(QString type)
Chris@316 460 {
Chris@316 461 return (type == "audio/mpeg");
Chris@316 462 }
Chris@316 463
Chris@316 464 bool
Chris@317 465 MP3FileReader::supports(FileSource &source)
Chris@316 466 {
Chris@316 467 return (supportsExtension(source.getExtension()) ||
Chris@316 468 supportsContentType(source.getContentType()));
Chris@316 469 }
Chris@316 470
Chris@316 471
Chris@148 472 #endif