annotate data/fileio/MP3FileReader.cpp @ 1188:d9698ee93659 spectrogram-minor-refactor

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