annotate data/fileio/MP3FileReader.cpp @ 294:2c1e57ad86e7

* Show colour swatch next to layer name in pane (if available) * Fix for incorrect layer name prefix handling (was making some layers appear to have the same model name in cases where the model names differed by the final character only)
author Chris Cannam
date Wed, 05 Sep 2007 15:17:15 +0000
parents 92e8dbde73cd
children c022976d18e8
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@290 37 MP3FileReader::MP3FileReader(QString path, DecodeMode decodeMode, CacheMode mode) :
Chris@148 38 CodedAudioFileReader(mode),
Chris@264 39 m_path(path),
Chris@264 40 m_decodeThread(0)
Chris@148 41 {
Chris@148 42 m_frameCount = 0;
Chris@148 43 m_channelCount = 0;
Chris@148 44 m_sampleRate = 0;
Chris@148 45 m_fileSize = 0;
Chris@148 46 m_bitrateNum = 0;
Chris@148 47 m_bitrateDenom = 0;
Chris@148 48 m_frameCount = 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@290 55 if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
Chris@290 56 m_error = QString("File %1 does not exist.").arg(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@290 63 if ((fd = ::open(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@290 68 m_error = QString("Failed to open file %1 for reading.").arg(path);
Chris@148 69 return;
Chris@148 70 }
Chris@148 71
Chris@263 72 m_filebuffer = 0;
Chris@148 73
Chris@148 74 try {
Chris@263 75 m_filebuffer = new unsigned char[m_fileSize];
Chris@148 76 } catch (...) {
Chris@290 77 m_error = QString("Out of memory");
Chris@148 78 ::close(fd);
Chris@148 79 return;
Chris@148 80 }
Chris@148 81
Chris@229 82 ssize_t sz = 0;
Chris@229 83 size_t offset = 0;
Chris@229 84 while (offset < m_fileSize) {
Chris@263 85 sz = ::read(fd, m_filebuffer + offset, m_fileSize - offset);
Chris@229 86 if (sz < 0) {
Chris@290 87 m_error = QString("Read error for file %1 (after %2 bytes)")
Chris@290 88 .arg(path).arg(offset);
Chris@263 89 delete[] m_filebuffer;
Chris@229 90 ::close(fd);
Chris@229 91 return;
Chris@230 92 } else if (sz == 0) {
Chris@231 93 std::cerr << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
Chris@231 94 .arg(offset).arg(m_fileSize).toStdString() << std::endl;
Chris@231 95 m_fileSize = offset;
Chris@230 96 break;
Chris@229 97 }
Chris@229 98 offset += sz;
Chris@148 99 }
Chris@148 100
Chris@148 101 ::close(fd);
Chris@148 102
Chris@271 103 loadTags();
Chris@271 104
Chris@263 105 if (decodeMode == DecodeAtOnce) {
Chris@263 106
Chris@148 107 m_progress = new QProgressDialog
Chris@290 108 (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
Chris@148 109 QObject::tr("Stop"), 0, 100);
Chris@148 110 m_progress->hide();
Chris@148 111
Chris@263 112 if (!decode(m_filebuffer, m_fileSize)) {
Chris@290 113 m_error = QString("Failed to decode file %1.").arg(path);
Chris@263 114 }
Chris@263 115
Chris@263 116 delete[] m_filebuffer;
Chris@263 117 m_filebuffer = 0;
Chris@148 118
Chris@263 119 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@263 120
Chris@148 121 delete m_progress;
Chris@148 122 m_progress = 0;
Chris@263 123
Chris@263 124 } else {
Chris@263 125
Chris@263 126 m_decodeThread = new DecodeThread(this);
Chris@263 127 m_decodeThread->start();
Chris@263 128
Chris@263 129 while (m_channelCount == 0 && !m_done) {
Chris@263 130 usleep(10);
Chris@263 131 }
Chris@148 132 }
Chris@148 133 }
Chris@148 134
Chris@148 135 MP3FileReader::~MP3FileReader()
Chris@148 136 {
Chris@263 137 if (m_decodeThread) {
Chris@265 138 m_cancelled = true;
Chris@263 139 m_decodeThread->wait();
Chris@263 140 delete m_decodeThread;
Chris@263 141 }
Chris@148 142 }
Chris@148 143
Chris@263 144 void
Chris@271 145 MP3FileReader::loadTags()
Chris@271 146 {
Chris@271 147 m_title = "";
Chris@271 148
Chris@271 149 #ifdef HAVE_ID3TAG
Chris@271 150
Chris@290 151 id3_file *file = id3_file_open(m_path.toLocal8Bit().data(),
Chris@271 152 ID3_FILE_MODE_READONLY);
Chris@271 153 if (!file) return;
Chris@271 154
Chris@273 155 // We can do this a lot more elegantly, but we'll leave that for
Chris@273 156 // when we implement support for more than just the one tag!
Chris@273 157
Chris@271 158 id3_tag *tag = id3_file_tag(file);
Chris@273 159 if (!tag) {
Chris@273 160 #ifdef DEBUG_ID3TAG
Chris@273 161 std::cerr << "MP3FileReader::loadTags: No ID3 tag found" << std::endl;
Chris@273 162 #endif
Chris@273 163 id3_file_close(file);
Chris@273 164 return;
Chris@271 165 }
Chris@271 166
Chris@273 167 id3_frame *frame = id3_tag_findframe(tag, "TIT2", 0); // work title
Chris@273 168 if (!frame) {
Chris@273 169 #ifdef DEBUG_ID3TAG
Chris@273 170 std::cerr << "MP3FileReader::loadTags: No work title in ID3 tag" << std::endl;
Chris@273 171 #endif
Chris@273 172 id3_file_close(file);
Chris@273 173 return;
Chris@273 174 }
Chris@273 175
Chris@273 176 if (frame->nfields < 2) {
Chris@273 177 std::cerr << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for work title in ID3 tag" << std::endl;
Chris@273 178 id3_file_close(file);
Chris@273 179 return;
Chris@273 180 }
Chris@273 181
Chris@273 182 unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
Chris@273 183 if (nstrings == 0) {
Chris@273 184 #ifdef DEBUG_ID3TAG
Chris@273 185 std::cerr << "MP3FileReader::loadTags: No data for work title in ID3 tag" << std::endl;
Chris@273 186 #endif
Chris@273 187 id3_file_close(file);
Chris@273 188 return;
Chris@273 189 }
Chris@273 190
Chris@273 191 id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
Chris@273 192 if (!ustr) {
Chris@273 193 #ifdef DEBUG_ID3TAG
Chris@273 194 std::cerr << "MP3FileReader::loadTags: Invalid or absent data for work title in ID3 tag" << std::endl;
Chris@273 195 #endif
Chris@273 196 id3_file_close(file);
Chris@273 197 return;
Chris@273 198 }
Chris@273 199
Chris@273 200 id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
Chris@273 201 if (!u8str) {
Chris@273 202 std::cerr << "MP3FileReader::loadTags: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 title" << std::endl;
Chris@273 203 id3_file_close(file);
Chris@273 204 return;
Chris@273 205 }
Chris@273 206
Chris@290 207 m_title = QString::fromUtf8((const char *)u8str);
Chris@273 208 free(u8str);
Chris@271 209 id3_file_close(file);
Chris@273 210
Chris@273 211 #else
Chris@273 212 #ifdef DEBUG_ID3TAG
Chris@273 213 std::cerr << "MP3FileReader::loadTags: ID3 tag support not compiled in"
Chris@273 214 << std::endl;
Chris@271 215 #endif
Chris@273 216 #endif
Chris@273 217
Chris@271 218 }
Chris@271 219
Chris@271 220 void
Chris@263 221 MP3FileReader::DecodeThread::run()
Chris@263 222 {
Chris@263 223 if (!m_reader->decode(m_reader->m_filebuffer, m_reader->m_fileSize)) {
Chris@290 224 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path);
Chris@263 225 }
Chris@263 226
Chris@263 227 delete[] m_reader->m_filebuffer;
Chris@263 228 m_reader->m_filebuffer = 0;
Chris@263 229
Chris@263 230 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@263 231
Chris@263 232 m_reader->m_done = true;
Chris@265 233 m_reader->m_completion = 100;
Chris@263 234 }
Chris@263 235
Chris@148 236 bool
Chris@148 237 MP3FileReader::decode(void *mm, size_t sz)
Chris@148 238 {
Chris@148 239 DecoderData data;
Chris@148 240 struct mad_decoder decoder;
Chris@148 241
Chris@148 242 data.start = (unsigned char const *)mm;
Chris@148 243 data.length = (unsigned long)sz;
Chris@148 244 data.reader = this;
Chris@148 245
Chris@148 246 mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0);
Chris@148 247 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
Chris@148 248 mad_decoder_finish(&decoder);
Chris@148 249
Chris@263 250 m_done = true;
Chris@148 251 return true;
Chris@148 252 }
Chris@148 253
Chris@148 254 enum mad_flow
Chris@148 255 MP3FileReader::input(void *dp, struct mad_stream *stream)
Chris@148 256 {
Chris@148 257 DecoderData *data = (DecoderData *)dp;
Chris@148 258
Chris@148 259 if (!data->length) return MAD_FLOW_STOP;
Chris@148 260 mad_stream_buffer(stream, data->start, data->length);
Chris@148 261 data->length = 0;
Chris@148 262
Chris@148 263 return MAD_FLOW_CONTINUE;
Chris@148 264 }
Chris@148 265
Chris@148 266 enum mad_flow
Chris@148 267 MP3FileReader::output(void *dp,
Chris@148 268 struct mad_header const *header,
Chris@148 269 struct mad_pcm *pcm)
Chris@148 270 {
Chris@148 271 DecoderData *data = (DecoderData *)dp;
Chris@148 272 return data->reader->accept(header, pcm);
Chris@148 273 }
Chris@148 274
Chris@148 275 enum mad_flow
Chris@148 276 MP3FileReader::accept(struct mad_header const *header,
Chris@148 277 struct mad_pcm *pcm)
Chris@148 278 {
Chris@148 279 int channels = pcm->channels;
Chris@148 280 int frames = pcm->length;
Chris@148 281
Chris@148 282 if (header) {
Chris@148 283 m_bitrateNum += header->bitrate;
Chris@148 284 m_bitrateDenom ++;
Chris@148 285 }
Chris@148 286
Chris@148 287 if (frames < 1) return MAD_FLOW_CONTINUE;
Chris@148 288
Chris@148 289 if (m_channelCount == 0) {
Chris@148 290 m_channelCount = channels;
Chris@148 291 m_sampleRate = pcm->samplerate;
Chris@148 292 }
Chris@148 293
Chris@148 294 if (m_bitrateDenom > 0) {
Chris@148 295 double bitrate = m_bitrateNum / m_bitrateDenom;
Chris@148 296 double duration = double(m_fileSize * 8) / bitrate;
Chris@148 297 double elapsed = double(m_frameCount) / m_sampleRate;
Chris@148 298 double percent = ((elapsed * 100.0) / duration);
Chris@265 299 int progress = int(percent);
Chris@265 300 if (progress < 1) progress = 1;
Chris@265 301 if (progress > 99) progress = 99;
Chris@265 302 m_completion = progress;
Chris@263 303 if (m_progress) {
Chris@263 304 if (progress > m_progress->value()) {
Chris@263 305 m_progress->setValue(progress);
Chris@263 306 m_progress->show();
Chris@263 307 m_progress->raise();
Chris@263 308 qApp->processEvents();
Chris@263 309 if (m_progress->wasCanceled()) {
Chris@263 310 m_cancelled = true;
Chris@263 311 }
Chris@148 312 }
Chris@148 313 }
Chris@148 314 }
Chris@148 315
Chris@148 316 if (m_cancelled) return MAD_FLOW_STOP;
Chris@148 317
Chris@148 318 m_frameCount += frames;
Chris@148 319
Chris@148 320 if (!isDecodeCacheInitialised()) {
Chris@148 321 initialiseDecodeCache();
Chris@148 322 }
Chris@148 323
Chris@148 324 for (int i = 0; i < frames; ++i) {
Chris@148 325
Chris@148 326 for (int ch = 0; ch < channels; ++ch) {
Chris@148 327 mad_fixed_t sample = 0;
Chris@148 328 if (ch < int(sizeof(pcm->samples) / sizeof(pcm->samples[0]))) {
Chris@148 329 sample = pcm->samples[ch][i];
Chris@148 330 }
Chris@148 331 float fsample = float(sample) / float(MAD_F_ONE);
Chris@148 332 addSampleToDecodeCache(fsample);
Chris@148 333 }
Chris@148 334
Chris@148 335 if (! (i & 0xffff)) {
Chris@148 336 // periodically munlock to ensure we don't exhaust real memory
Chris@148 337 // if running with memory locked down
Chris@148 338 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 339 }
Chris@148 340 }
Chris@148 341
Chris@148 342 if (frames > 0) {
Chris@148 343 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 344 }
Chris@148 345
Chris@148 346 return MAD_FLOW_CONTINUE;
Chris@148 347 }
Chris@148 348
Chris@148 349 enum mad_flow
Chris@148 350 MP3FileReader::error(void *dp,
Chris@148 351 struct mad_stream *stream,
Chris@148 352 struct mad_frame *)
Chris@148 353 {
Chris@148 354 DecoderData *data = (DecoderData *)dp;
Chris@148 355
Chris@148 356 fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
Chris@148 357 stream->error, mad_stream_errorstr(stream),
Chris@148 358 stream->this_frame - data->start);
Chris@148 359
Chris@148 360 return MAD_FLOW_CONTINUE;
Chris@148 361 }
Chris@148 362
Chris@157 363 void
Chris@290 364 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 365 {
Chris@157 366 extensions.insert("mp3");
Chris@157 367 }
Chris@157 368
Chris@148 369 #endif