annotate data/fileio/MP3FileReader.cpp @ 795:dc20458f6f85 qt5

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