annotate data/fileio/MP3FileReader.cpp @ 631:3a5ee4b6c9ad

* Complete the overhaul of CSV file import; now you can pick the purpose for each column in the file, and SV should do the rest. The most significant practical improvement here is that we can now handle files in which time and duration do not necessarily appear in known columns.
author Chris Cannam
date Mon, 19 Jul 2010 17:08:56 +0000
parents 0a9e960fca53
children a4b8ad0f1a8f
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@231 99 .arg(offset).arg(m_fileSize).toStdString() << 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@499 144 std::cerr << "MP3FileReader::MP3FileReader(\"" << m_path.toStdString() << "\"): ERROR: " << m_error.toStdString() << 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@273 180 std::cerr << "MP3FileReader::loadTags: No ID3 tag found" << std::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@271 192 id3_file_close(file);
Chris@273 193
Chris@273 194 #else
Chris@273 195 #ifdef DEBUG_ID3TAG
Chris@273 196 std::cerr << "MP3FileReader::loadTags: ID3 tag support not compiled in"
Chris@273 197 << std::endl;
Chris@271 198 #endif
Chris@273 199 #endif
Chris@333 200 }
Chris@273 201
Chris@333 202 QString
Chris@333 203 MP3FileReader::loadTag(void *vtag, const char *name)
Chris@333 204 {
Chris@333 205 #ifdef HAVE_ID3TAG
Chris@333 206 id3_tag *tag = (id3_tag *)vtag;
Chris@333 207
Chris@333 208 id3_frame *frame = id3_tag_findframe(tag, name, 0);
Chris@333 209 if (!frame) {
Chris@333 210 #ifdef DEBUG_ID3TAG
Chris@333 211 std::cerr << "MP3FileReader::loadTags: No \"" << name << "\" in ID3 tag" << std::endl;
Chris@333 212 #endif
Chris@333 213 return "";
Chris@333 214 }
Chris@333 215
Chris@333 216 if (frame->nfields < 2) {
Chris@333 217 std::cerr << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << std::endl;
Chris@333 218 return "";
Chris@333 219 }
Chris@333 220
Chris@333 221 unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
Chris@333 222 if (nstrings == 0) {
Chris@333 223 #ifdef DEBUG_ID3TAG
Chris@348 224 std::cerr << "MP3FileReader::loadTags: No strings for \"" << name << "\" in ID3 tag" << std::endl;
Chris@333 225 #endif
Chris@333 226 return "";
Chris@333 227 }
Chris@333 228
Chris@333 229 id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
Chris@333 230 if (!ustr) {
Chris@333 231 #ifdef DEBUG_ID3TAG
Chris@333 232 std::cerr << "MP3FileReader::loadTags: Invalid or absent data for \"" << name << "\" in ID3 tag" << std::endl;
Chris@333 233 #endif
Chris@333 234 return "";
Chris@333 235 }
Chris@333 236
Chris@333 237 id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
Chris@333 238 if (!u8str) {
Chris@333 239 std::cerr << "MP3FileReader::loadTags: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 title" << std::endl;
Chris@333 240 return "";
Chris@333 241 }
Chris@333 242
Chris@333 243 QString rv = QString::fromUtf8((const char *)u8str);
Chris@333 244 free(u8str);
Chris@334 245
Chris@334 246 #ifdef DEBUG_ID3TAG
Chris@334 247 std::cerr << "MP3FileReader::loadTags: tag \"" << name << "\" -> \""
Chris@334 248 << rv.toStdString() << "\"" << std::endl;
Chris@334 249 #endif
Chris@334 250
Chris@334 251
Chris@333 252 return rv;
Chris@333 253
Chris@333 254 #else
Chris@333 255 return "";
Chris@333 256 #endif
Chris@271 257 }
Chris@271 258
Chris@271 259 void
Chris@263 260 MP3FileReader::DecodeThread::run()
Chris@263 261 {
Chris@263 262 if (!m_reader->decode(m_reader->m_filebuffer, m_reader->m_fileSize)) {
Chris@290 263 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path);
Chris@263 264 }
Chris@263 265
Chris@263 266 delete[] m_reader->m_filebuffer;
Chris@263 267 m_reader->m_filebuffer = 0;
Chris@297 268
Chris@297 269 if (m_reader->m_samplebuffer) {
Chris@297 270 for (size_t c = 0; c < m_reader->m_channelCount; ++c) {
Chris@297 271 delete[] m_reader->m_samplebuffer[c];
Chris@297 272 }
Chris@297 273 delete[] m_reader->m_samplebuffer;
Chris@297 274 m_reader->m_samplebuffer = 0;
Chris@297 275 }
Chris@297 276
Chris@263 277 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@263 278
Chris@263 279 m_reader->m_done = true;
Chris@265 280 m_reader->m_completion = 100;
Chris@297 281
Chris@297 282 m_reader->endSerialised();
Chris@263 283 }
Chris@263 284
Chris@148 285 bool
Chris@148 286 MP3FileReader::decode(void *mm, size_t sz)
Chris@148 287 {
Chris@148 288 DecoderData data;
Chris@148 289 struct mad_decoder decoder;
Chris@148 290
Chris@148 291 data.start = (unsigned char const *)mm;
Chris@148 292 data.length = (unsigned long)sz;
Chris@148 293 data.reader = this;
Chris@148 294
Chris@148 295 mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0);
Chris@148 296 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
Chris@148 297 mad_decoder_finish(&decoder);
Chris@148 298
Chris@263 299 m_done = true;
Chris@148 300 return true;
Chris@148 301 }
Chris@148 302
Chris@148 303 enum mad_flow
Chris@148 304 MP3FileReader::input(void *dp, struct mad_stream *stream)
Chris@148 305 {
Chris@148 306 DecoderData *data = (DecoderData *)dp;
Chris@148 307
Chris@148 308 if (!data->length) return MAD_FLOW_STOP;
Chris@348 309
Chris@348 310 unsigned char const *start = data->start;
Chris@348 311 unsigned long length = data->length;
Chris@348 312
Chris@348 313 #ifdef HAVE_ID3TAG
Chris@348 314 if (length > ID3_TAG_QUERYSIZE) {
Chris@348 315 int taglen = id3_tag_query(start, ID3_TAG_QUERYSIZE);
Chris@348 316 if (taglen > 0) {
Chris@348 317 // std::cerr << "ID3 tag length to skip: " << taglen << std::endl;
Chris@348 318 start += taglen;
Chris@348 319 length -= taglen;
Chris@348 320 }
Chris@348 321 }
Chris@348 322 #endif
Chris@348 323
Chris@348 324 mad_stream_buffer(stream, start, length);
Chris@148 325 data->length = 0;
Chris@148 326
Chris@148 327 return MAD_FLOW_CONTINUE;
Chris@148 328 }
Chris@148 329
Chris@148 330 enum mad_flow
Chris@148 331 MP3FileReader::output(void *dp,
Chris@148 332 struct mad_header const *header,
Chris@148 333 struct mad_pcm *pcm)
Chris@148 334 {
Chris@148 335 DecoderData *data = (DecoderData *)dp;
Chris@148 336 return data->reader->accept(header, pcm);
Chris@148 337 }
Chris@148 338
Chris@148 339 enum mad_flow
Chris@148 340 MP3FileReader::accept(struct mad_header const *header,
Chris@148 341 struct mad_pcm *pcm)
Chris@148 342 {
Chris@148 343 int channels = pcm->channels;
Chris@148 344 int frames = pcm->length;
Chris@148 345
Chris@148 346 if (header) {
Chris@148 347 m_bitrateNum += header->bitrate;
Chris@148 348 m_bitrateDenom ++;
Chris@148 349 }
Chris@148 350
Chris@148 351 if (frames < 1) return MAD_FLOW_CONTINUE;
Chris@148 352
Chris@148 353 if (m_channelCount == 0) {
Chris@297 354
Chris@297 355 m_fileRate = pcm->samplerate;
Chris@148 356 m_channelCount = channels;
Chris@297 357
Chris@297 358 initialiseDecodeCache();
Chris@297 359
Chris@297 360 if (m_cacheMode == CacheInTemporaryFile) {
Chris@522 361 // std::cerr << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << std::endl;
Chris@297 362 startSerialised("MP3FileReader::Decode");
Chris@297 363 }
Chris@148 364 }
Chris@148 365
Chris@148 366 if (m_bitrateDenom > 0) {
Chris@148 367 double bitrate = m_bitrateNum / m_bitrateDenom;
Chris@148 368 double duration = double(m_fileSize * 8) / bitrate;
Chris@148 369 double elapsed = double(m_frameCount) / m_sampleRate;
Chris@375 370 double percent = 100;
Chris@375 371 if (duration > 0.0) percent = ((elapsed * 100.0) / duration);
Chris@357 372 int p = int(percent);
Chris@357 373 if (p < 1) p = 1;
Chris@357 374 if (p > 99) p = 99;
Chris@392 375 if (m_completion != p && m_reporter) {
Chris@357 376 m_completion = p;
Chris@392 377 m_reporter->setProgress(m_completion);
Chris@148 378 }
Chris@148 379 }
Chris@148 380
Chris@148 381 if (m_cancelled) return MAD_FLOW_STOP;
Chris@148 382
Chris@148 383 if (!isDecodeCacheInitialised()) {
Chris@148 384 initialiseDecodeCache();
Chris@148 385 }
Chris@148 386
Chris@297 387 if (m_samplebuffersize < frames) {
Chris@297 388 if (!m_samplebuffer) {
Chris@297 389 m_samplebuffer = new float *[channels];
Chris@297 390 for (int c = 0; c < channels; ++c) {
Chris@297 391 m_samplebuffer[c] = 0;
Chris@297 392 }
Chris@297 393 }
Chris@297 394 for (int c = 0; c < channels; ++c) {
Chris@297 395 delete[] m_samplebuffer[c];
Chris@297 396 m_samplebuffer[c] = new float[frames];
Chris@297 397 }
Chris@297 398 m_samplebuffersize = frames;
Chris@297 399 }
Chris@148 400
Chris@297 401 int activeChannels = int(sizeof(pcm->samples) / sizeof(pcm->samples[0]));
Chris@297 402
Chris@297 403 for (int ch = 0; ch < channels; ++ch) {
Chris@297 404
Chris@297 405 for (int i = 0; i < frames; ++i) {
Chris@297 406
Chris@148 407 mad_fixed_t sample = 0;
Chris@297 408 if (ch < activeChannels) {
Chris@148 409 sample = pcm->samples[ch][i];
Chris@148 410 }
Chris@148 411 float fsample = float(sample) / float(MAD_F_ONE);
Chris@297 412
Chris@297 413 m_samplebuffer[ch][i] = fsample;
Chris@148 414 }
Chris@148 415 }
Chris@148 416
Chris@297 417 addSamplesToDecodeCache(m_samplebuffer, frames);
Chris@148 418
Chris@148 419 return MAD_FLOW_CONTINUE;
Chris@148 420 }
Chris@148 421
Chris@148 422 enum mad_flow
Chris@148 423 MP3FileReader::error(void *dp,
Chris@148 424 struct mad_stream *stream,
Chris@148 425 struct mad_frame *)
Chris@148 426 {
Chris@148 427 DecoderData *data = (DecoderData *)dp;
Chris@148 428
Chris@523 429 // fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %lu\n",
Chris@523 430 // stream->error, mad_stream_errorstr(stream),
Chris@523 431 // (unsigned long)(stream->this_frame - data->start));
Chris@148 432
Chris@148 433 return MAD_FLOW_CONTINUE;
Chris@148 434 }
Chris@148 435
Chris@157 436 void
Chris@290 437 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 438 {
Chris@157 439 extensions.insert("mp3");
Chris@157 440 }
Chris@157 441
Chris@316 442 bool
Chris@316 443 MP3FileReader::supportsExtension(QString extension)
Chris@316 444 {
Chris@316 445 std::set<QString> extensions;
Chris@316 446 getSupportedExtensions(extensions);
Chris@316 447 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 448 }
Chris@316 449
Chris@316 450 bool
Chris@316 451 MP3FileReader::supportsContentType(QString type)
Chris@316 452 {
Chris@316 453 return (type == "audio/mpeg");
Chris@316 454 }
Chris@316 455
Chris@316 456 bool
Chris@317 457 MP3FileReader::supports(FileSource &source)
Chris@316 458 {
Chris@316 459 return (supportsExtension(source.getExtension()) ||
Chris@316 460 supportsContentType(source.getContentType()));
Chris@316 461 }
Chris@316 462
Chris@316 463
Chris@148 464 #endif