annotate data/fileio/MP3FileReader.cpp @ 316:3a6725f285d6

* Make RemoteFile far more pervasive, and use it for local files as well so that we can handle both transparently. Make it shallow copy with reference counting, so it can be used by value without having to worry about the cache file lifetime. Use RemoteFile for MainWindow file-open functions, etc
author Chris Cannam
date Thu, 18 Oct 2007 15:31:20 +0000
parents c022976d18e8
children c324d410b096
rev   line source
Chris@148 1
Chris@148 2 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 3
Chris@148 4 /*
Chris@148 5 Sonic Visualiser
Chris@148 6 An audio file viewer and annotation editor.
Chris@148 7 Centre for Digital Music, Queen Mary, University of London.
Chris@148 8 This file copyright 2006 Chris Cannam.
Chris@148 9
Chris@148 10 This program is free software; you can redistribute it and/or
Chris@148 11 modify it under the terms of the GNU General Public License as
Chris@148 12 published by the Free Software Foundation; either version 2 of the
Chris@148 13 License, or (at your option) any later version. See the file
Chris@148 14 COPYING included with this distribution for more information.
Chris@148 15 */
Chris@148 16
Chris@148 17 #ifdef HAVE_MAD
Chris@148 18
Chris@148 19 #include "MP3FileReader.h"
Chris@150 20 #include "system/System.h"
Chris@148 21
Chris@148 22 #include <sys/types.h>
Chris@148 23 #include <sys/stat.h>
Chris@148 24 #include <fcntl.h>
Chris@148 25
Chris@148 26 #include <iostream>
Chris@148 27
Chris@271 28 #ifdef HAVE_ID3TAG
Chris@271 29 #include <id3tag.h>
Chris@271 30 #endif
Chris@273 31 #define DEBUG_ID3TAG 1
Chris@271 32
Chris@148 33 #include <QApplication>
Chris@186 34 #include <QFileInfo>
Chris@148 35 #include <QProgressDialog>
Chris@148 36
Chris@316 37 MP3FileReader::MP3FileReader(RemoteFile source, DecodeMode decodeMode,
Chris@297 38 CacheMode mode, size_t targetRate) :
Chris@297 39 CodedAudioFileReader(mode, targetRate),
Chris@316 40 m_source(source),
Chris@316 41 m_path(source.getLocalFilename()),
Chris@264 42 m_decodeThread(0)
Chris@148 43 {
Chris@148 44 m_channelCount = 0;
Chris@297 45 m_fileRate = 0;
Chris@148 46 m_fileSize = 0;
Chris@148 47 m_bitrateNum = 0;
Chris@148 48 m_bitrateDenom = 0;
Chris@148 49 m_cancelled = false;
Chris@265 50 m_completion = 0;
Chris@263 51 m_done = false;
Chris@263 52 m_progress = 0;
Chris@148 53
Chris@148 54 struct stat stat;
Chris@316 55 if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
Chris@316 56 m_error = QString("File %1 does not exist.").arg(m_path);
Chris@148 57 return;
Chris@148 58 }
Chris@148 59
Chris@148 60 m_fileSize = stat.st_size;
Chris@148 61
Chris@229 62 int fd = -1;
Chris@316 63 if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY
Chris@231 64 #ifdef _WIN32
Chris@231 65 | O_BINARY
Chris@231 66 #endif
Chris@231 67 , 0)) < 0) {
Chris@316 68 m_error = QString("Failed to open file %1 for reading.").arg(m_path);
Chris@148 69 return;
Chris@148 70 }
Chris@148 71
Chris@263 72 m_filebuffer = 0;
Chris@297 73 m_samplebuffer = 0;
Chris@297 74 m_samplebuffersize = 0;
Chris@148 75
Chris@148 76 try {
Chris@263 77 m_filebuffer = new unsigned char[m_fileSize];
Chris@148 78 } catch (...) {
Chris@290 79 m_error = QString("Out of memory");
Chris@148 80 ::close(fd);
Chris@148 81 return;
Chris@148 82 }
Chris@148 83
Chris@229 84 ssize_t sz = 0;
Chris@229 85 size_t offset = 0;
Chris@229 86 while (offset < m_fileSize) {
Chris@263 87 sz = ::read(fd, m_filebuffer + offset, m_fileSize - offset);
Chris@229 88 if (sz < 0) {
Chris@290 89 m_error = QString("Read error for file %1 (after %2 bytes)")
Chris@316 90 .arg(m_path).arg(offset);
Chris@263 91 delete[] m_filebuffer;
Chris@229 92 ::close(fd);
Chris@229 93 return;
Chris@230 94 } else if (sz == 0) {
Chris@231 95 std::cerr << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
Chris@231 96 .arg(offset).arg(m_fileSize).toStdString() << std::endl;
Chris@231 97 m_fileSize = offset;
Chris@230 98 break;
Chris@229 99 }
Chris@229 100 offset += sz;
Chris@148 101 }
Chris@148 102
Chris@148 103 ::close(fd);
Chris@148 104
Chris@271 105 loadTags();
Chris@271 106
Chris@263 107 if (decodeMode == DecodeAtOnce) {
Chris@263 108
Chris@148 109 m_progress = new QProgressDialog
Chris@316 110 (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()),
Chris@148 111 QObject::tr("Stop"), 0, 100);
Chris@148 112 m_progress->hide();
Chris@148 113
Chris@263 114 if (!decode(m_filebuffer, m_fileSize)) {
Chris@316 115 m_error = QString("Failed to decode file %1.").arg(m_path);
Chris@263 116 }
Chris@263 117
Chris@263 118 delete[] m_filebuffer;
Chris@263 119 m_filebuffer = 0;
Chris@148 120
Chris@263 121 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@263 122
Chris@148 123 delete m_progress;
Chris@148 124 m_progress = 0;
Chris@263 125
Chris@263 126 } else {
Chris@263 127
Chris@263 128 m_decodeThread = new DecodeThread(this);
Chris@263 129 m_decodeThread->start();
Chris@263 130
Chris@263 131 while (m_channelCount == 0 && !m_done) {
Chris@263 132 usleep(10);
Chris@263 133 }
Chris@148 134 }
Chris@148 135 }
Chris@148 136
Chris@148 137 MP3FileReader::~MP3FileReader()
Chris@148 138 {
Chris@263 139 if (m_decodeThread) {
Chris@265 140 m_cancelled = true;
Chris@263 141 m_decodeThread->wait();
Chris@263 142 delete m_decodeThread;
Chris@263 143 }
Chris@148 144 }
Chris@148 145
Chris@263 146 void
Chris@271 147 MP3FileReader::loadTags()
Chris@271 148 {
Chris@271 149 m_title = "";
Chris@271 150
Chris@271 151 #ifdef HAVE_ID3TAG
Chris@271 152
Chris@290 153 id3_file *file = id3_file_open(m_path.toLocal8Bit().data(),
Chris@271 154 ID3_FILE_MODE_READONLY);
Chris@271 155 if (!file) return;
Chris@271 156
Chris@273 157 // We can do this a lot more elegantly, but we'll leave that for
Chris@273 158 // when we implement support for more than just the one tag!
Chris@273 159
Chris@271 160 id3_tag *tag = id3_file_tag(file);
Chris@273 161 if (!tag) {
Chris@273 162 #ifdef DEBUG_ID3TAG
Chris@273 163 std::cerr << "MP3FileReader::loadTags: No ID3 tag found" << std::endl;
Chris@273 164 #endif
Chris@273 165 id3_file_close(file);
Chris@273 166 return;
Chris@271 167 }
Chris@271 168
Chris@273 169 id3_frame *frame = id3_tag_findframe(tag, "TIT2", 0); // work title
Chris@273 170 if (!frame) {
Chris@273 171 #ifdef DEBUG_ID3TAG
Chris@273 172 std::cerr << "MP3FileReader::loadTags: No work title in ID3 tag" << std::endl;
Chris@273 173 #endif
Chris@273 174 id3_file_close(file);
Chris@273 175 return;
Chris@273 176 }
Chris@273 177
Chris@273 178 if (frame->nfields < 2) {
Chris@273 179 std::cerr << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for work title in ID3 tag" << std::endl;
Chris@273 180 id3_file_close(file);
Chris@273 181 return;
Chris@273 182 }
Chris@273 183
Chris@273 184 unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
Chris@273 185 if (nstrings == 0) {
Chris@273 186 #ifdef DEBUG_ID3TAG
Chris@273 187 std::cerr << "MP3FileReader::loadTags: No data for work title in ID3 tag" << std::endl;
Chris@273 188 #endif
Chris@273 189 id3_file_close(file);
Chris@273 190 return;
Chris@273 191 }
Chris@273 192
Chris@273 193 id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
Chris@273 194 if (!ustr) {
Chris@273 195 #ifdef DEBUG_ID3TAG
Chris@273 196 std::cerr << "MP3FileReader::loadTags: Invalid or absent data for work title in ID3 tag" << std::endl;
Chris@273 197 #endif
Chris@273 198 id3_file_close(file);
Chris@273 199 return;
Chris@273 200 }
Chris@273 201
Chris@273 202 id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
Chris@273 203 if (!u8str) {
Chris@273 204 std::cerr << "MP3FileReader::loadTags: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 title" << std::endl;
Chris@273 205 id3_file_close(file);
Chris@273 206 return;
Chris@273 207 }
Chris@273 208
Chris@290 209 m_title = QString::fromUtf8((const char *)u8str);
Chris@273 210 free(u8str);
Chris@271 211 id3_file_close(file);
Chris@273 212
Chris@273 213 #else
Chris@273 214 #ifdef DEBUG_ID3TAG
Chris@273 215 std::cerr << "MP3FileReader::loadTags: ID3 tag support not compiled in"
Chris@273 216 << std::endl;
Chris@271 217 #endif
Chris@273 218 #endif
Chris@273 219
Chris@271 220 }
Chris@271 221
Chris@271 222 void
Chris@263 223 MP3FileReader::DecodeThread::run()
Chris@263 224 {
Chris@263 225 if (!m_reader->decode(m_reader->m_filebuffer, m_reader->m_fileSize)) {
Chris@290 226 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path);
Chris@263 227 }
Chris@263 228
Chris@263 229 delete[] m_reader->m_filebuffer;
Chris@263 230 m_reader->m_filebuffer = 0;
Chris@297 231
Chris@297 232 if (m_reader->m_samplebuffer) {
Chris@297 233 for (size_t c = 0; c < m_reader->m_channelCount; ++c) {
Chris@297 234 delete[] m_reader->m_samplebuffer[c];
Chris@297 235 }
Chris@297 236 delete[] m_reader->m_samplebuffer;
Chris@297 237 m_reader->m_samplebuffer = 0;
Chris@297 238 }
Chris@297 239
Chris@263 240 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@263 241
Chris@263 242 m_reader->m_done = true;
Chris@265 243 m_reader->m_completion = 100;
Chris@297 244
Chris@297 245 m_reader->endSerialised();
Chris@263 246 }
Chris@263 247
Chris@148 248 bool
Chris@148 249 MP3FileReader::decode(void *mm, size_t sz)
Chris@148 250 {
Chris@148 251 DecoderData data;
Chris@148 252 struct mad_decoder decoder;
Chris@148 253
Chris@148 254 data.start = (unsigned char const *)mm;
Chris@148 255 data.length = (unsigned long)sz;
Chris@148 256 data.reader = this;
Chris@148 257
Chris@148 258 mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0);
Chris@148 259 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
Chris@148 260 mad_decoder_finish(&decoder);
Chris@148 261
Chris@263 262 m_done = true;
Chris@148 263 return true;
Chris@148 264 }
Chris@148 265
Chris@148 266 enum mad_flow
Chris@148 267 MP3FileReader::input(void *dp, struct mad_stream *stream)
Chris@148 268 {
Chris@148 269 DecoderData *data = (DecoderData *)dp;
Chris@148 270
Chris@148 271 if (!data->length) return MAD_FLOW_STOP;
Chris@148 272 mad_stream_buffer(stream, data->start, data->length);
Chris@148 273 data->length = 0;
Chris@148 274
Chris@148 275 return MAD_FLOW_CONTINUE;
Chris@148 276 }
Chris@148 277
Chris@148 278 enum mad_flow
Chris@148 279 MP3FileReader::output(void *dp,
Chris@148 280 struct mad_header const *header,
Chris@148 281 struct mad_pcm *pcm)
Chris@148 282 {
Chris@148 283 DecoderData *data = (DecoderData *)dp;
Chris@148 284 return data->reader->accept(header, pcm);
Chris@148 285 }
Chris@148 286
Chris@148 287 enum mad_flow
Chris@148 288 MP3FileReader::accept(struct mad_header const *header,
Chris@148 289 struct mad_pcm *pcm)
Chris@148 290 {
Chris@148 291 int channels = pcm->channels;
Chris@148 292 int frames = pcm->length;
Chris@148 293
Chris@148 294 if (header) {
Chris@148 295 m_bitrateNum += header->bitrate;
Chris@148 296 m_bitrateDenom ++;
Chris@148 297 }
Chris@148 298
Chris@148 299 if (frames < 1) return MAD_FLOW_CONTINUE;
Chris@148 300
Chris@148 301 if (m_channelCount == 0) {
Chris@297 302
Chris@297 303 m_fileRate = pcm->samplerate;
Chris@148 304 m_channelCount = channels;
Chris@297 305
Chris@297 306 initialiseDecodeCache();
Chris@297 307
Chris@297 308 if (m_cacheMode == CacheInTemporaryFile) {
Chris@297 309 m_completion = 1;
Chris@297 310 std::cerr << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << std::endl;
Chris@297 311 startSerialised("MP3FileReader::Decode");
Chris@297 312 }
Chris@148 313 }
Chris@148 314
Chris@148 315 if (m_bitrateDenom > 0) {
Chris@148 316 double bitrate = m_bitrateNum / m_bitrateDenom;
Chris@148 317 double duration = double(m_fileSize * 8) / bitrate;
Chris@148 318 double elapsed = double(m_frameCount) / m_sampleRate;
Chris@148 319 double percent = ((elapsed * 100.0) / duration);
Chris@265 320 int progress = int(percent);
Chris@265 321 if (progress < 1) progress = 1;
Chris@265 322 if (progress > 99) progress = 99;
Chris@265 323 m_completion = progress;
Chris@263 324 if (m_progress) {
Chris@263 325 if (progress > m_progress->value()) {
Chris@263 326 m_progress->setValue(progress);
Chris@263 327 m_progress->show();
Chris@263 328 m_progress->raise();
Chris@263 329 qApp->processEvents();
Chris@263 330 if (m_progress->wasCanceled()) {
Chris@263 331 m_cancelled = true;
Chris@263 332 }
Chris@148 333 }
Chris@148 334 }
Chris@148 335 }
Chris@148 336
Chris@148 337 if (m_cancelled) return MAD_FLOW_STOP;
Chris@148 338
Chris@148 339 if (!isDecodeCacheInitialised()) {
Chris@148 340 initialiseDecodeCache();
Chris@148 341 }
Chris@148 342
Chris@297 343 if (m_samplebuffersize < frames) {
Chris@297 344 if (!m_samplebuffer) {
Chris@297 345 m_samplebuffer = new float *[channels];
Chris@297 346 for (int c = 0; c < channels; ++c) {
Chris@297 347 m_samplebuffer[c] = 0;
Chris@297 348 }
Chris@297 349 }
Chris@297 350 for (int c = 0; c < channels; ++c) {
Chris@297 351 delete[] m_samplebuffer[c];
Chris@297 352 m_samplebuffer[c] = new float[frames];
Chris@297 353 }
Chris@297 354 m_samplebuffersize = frames;
Chris@297 355 }
Chris@148 356
Chris@297 357 int activeChannels = int(sizeof(pcm->samples) / sizeof(pcm->samples[0]));
Chris@297 358
Chris@297 359 for (int ch = 0; ch < channels; ++ch) {
Chris@297 360
Chris@297 361 for (int i = 0; i < frames; ++i) {
Chris@297 362
Chris@148 363 mad_fixed_t sample = 0;
Chris@297 364 if (ch < activeChannels) {
Chris@148 365 sample = pcm->samples[ch][i];
Chris@148 366 }
Chris@148 367 float fsample = float(sample) / float(MAD_F_ONE);
Chris@297 368
Chris@297 369 m_samplebuffer[ch][i] = fsample;
Chris@148 370 }
Chris@148 371 }
Chris@148 372
Chris@297 373 addSamplesToDecodeCache(m_samplebuffer, frames);
Chris@148 374
Chris@148 375 return MAD_FLOW_CONTINUE;
Chris@148 376 }
Chris@148 377
Chris@148 378 enum mad_flow
Chris@148 379 MP3FileReader::error(void *dp,
Chris@148 380 struct mad_stream *stream,
Chris@148 381 struct mad_frame *)
Chris@148 382 {
Chris@148 383 DecoderData *data = (DecoderData *)dp;
Chris@148 384
Chris@148 385 fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
Chris@148 386 stream->error, mad_stream_errorstr(stream),
Chris@148 387 stream->this_frame - data->start);
Chris@148 388
Chris@148 389 return MAD_FLOW_CONTINUE;
Chris@148 390 }
Chris@148 391
Chris@157 392 void
Chris@290 393 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 394 {
Chris@157 395 extensions.insert("mp3");
Chris@157 396 }
Chris@157 397
Chris@316 398 bool
Chris@316 399 MP3FileReader::supportsExtension(QString extension)
Chris@316 400 {
Chris@316 401 std::set<QString> extensions;
Chris@316 402 getSupportedExtensions(extensions);
Chris@316 403 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 404 }
Chris@316 405
Chris@316 406 bool
Chris@316 407 MP3FileReader::supportsContentType(QString type)
Chris@316 408 {
Chris@316 409 return (type == "audio/mpeg");
Chris@316 410 }
Chris@316 411
Chris@316 412 bool
Chris@316 413 MP3FileReader::supports(RemoteFile &source)
Chris@316 414 {
Chris@316 415 return (supportsExtension(source.getExtension()) ||
Chris@316 416 supportsContentType(source.getContentType()));
Chris@316 417 }
Chris@316 418
Chris@316 419
Chris@148 420 #endif