annotate data/fileio/MP3FileReader.cpp @ 1248:58dd6a6fe414 piper

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