annotate data/fileio/MP3FileReader.cpp @ 308:14e0f60435b8

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