annotate data/fileio/MP3FileReader.cpp @ 263:71dfc6ab3b54

* Threaded mp3/ogg file reading. Not activated yet, as it doesn't work in context (SV needs to know the duration of its main model at the outset)
author Chris Cannam
date Thu, 24 May 2007 16:20:22 +0000
parents 0031495aba07
children 260032c26c4f
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@148 28 #include <QApplication>
Chris@186 29 #include <QFileInfo>
Chris@148 30 #include <QProgressDialog>
Chris@148 31
Chris@263 32 MP3FileReader::MP3FileReader(QString path, DecodeMode decodeMode, CacheMode mode) :
Chris@148 33 CodedAudioFileReader(mode),
Chris@148 34 m_path(path)
Chris@148 35 {
Chris@148 36 m_frameCount = 0;
Chris@148 37 m_channelCount = 0;
Chris@148 38 m_sampleRate = 0;
Chris@148 39 m_fileSize = 0;
Chris@148 40 m_bitrateNum = 0;
Chris@148 41 m_bitrateDenom = 0;
Chris@148 42 m_frameCount = 0;
Chris@148 43 m_cancelled = false;
Chris@263 44 m_done = false;
Chris@263 45 m_progress = 0;
Chris@148 46
Chris@148 47 struct stat stat;
Chris@148 48 if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
Chris@148 49 m_error = QString("File %1 does not exist.").arg(path);
Chris@148 50 return;
Chris@148 51 }
Chris@148 52
Chris@148 53 m_fileSize = stat.st_size;
Chris@148 54
Chris@229 55 int fd = -1;
Chris@231 56 if ((fd = ::open(path.toLocal8Bit().data(), O_RDONLY
Chris@231 57 #ifdef _WIN32
Chris@231 58 | O_BINARY
Chris@231 59 #endif
Chris@231 60 , 0)) < 0) {
Chris@148 61 m_error = QString("Failed to open file %1 for reading.").arg(path);
Chris@148 62 return;
Chris@148 63 }
Chris@148 64
Chris@263 65 m_filebuffer = 0;
Chris@148 66
Chris@148 67 try {
Chris@263 68 m_filebuffer = new unsigned char[m_fileSize];
Chris@148 69 } catch (...) {
Chris@148 70 m_error = QString("Out of memory");
Chris@148 71 ::close(fd);
Chris@148 72 return;
Chris@148 73 }
Chris@148 74
Chris@229 75 ssize_t sz = 0;
Chris@229 76 size_t offset = 0;
Chris@229 77 while (offset < m_fileSize) {
Chris@263 78 sz = ::read(fd, m_filebuffer + offset, m_fileSize - offset);
Chris@229 79 if (sz < 0) {
Chris@231 80 m_error = QString("Read error for file %1 (after %2 bytes)")
Chris@231 81 .arg(path).arg(offset);
Chris@263 82 delete[] m_filebuffer;
Chris@229 83 ::close(fd);
Chris@229 84 return;
Chris@230 85 } else if (sz == 0) {
Chris@231 86 std::cerr << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
Chris@231 87 .arg(offset).arg(m_fileSize).toStdString() << std::endl;
Chris@231 88 m_fileSize = offset;
Chris@230 89 break;
Chris@229 90 }
Chris@229 91 offset += sz;
Chris@148 92 }
Chris@148 93
Chris@148 94 ::close(fd);
Chris@148 95
Chris@263 96 if (decodeMode == DecodeAtOnce) {
Chris@263 97
Chris@148 98 m_progress = new QProgressDialog
Chris@186 99 (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
Chris@148 100 QObject::tr("Stop"), 0, 100);
Chris@148 101 m_progress->hide();
Chris@148 102
Chris@263 103 if (!decode(m_filebuffer, m_fileSize)) {
Chris@263 104 m_error = QString("Failed to decode file %1.").arg(path);
Chris@263 105 }
Chris@263 106
Chris@263 107 delete[] m_filebuffer;
Chris@263 108 m_filebuffer = 0;
Chris@148 109
Chris@263 110 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@263 111
Chris@148 112 delete m_progress;
Chris@148 113 m_progress = 0;
Chris@263 114
Chris@263 115 } else {
Chris@263 116
Chris@263 117 m_decodeThread = new DecodeThread(this);
Chris@263 118 m_decodeThread->start();
Chris@263 119
Chris@263 120 while (m_channelCount == 0 && !m_done) {
Chris@263 121 usleep(10);
Chris@263 122 }
Chris@148 123 }
Chris@148 124 }
Chris@148 125
Chris@148 126 MP3FileReader::~MP3FileReader()
Chris@148 127 {
Chris@263 128 if (m_decodeThread) {
Chris@263 129 m_decodeThread->wait();
Chris@263 130 delete m_decodeThread;
Chris@263 131 }
Chris@148 132 }
Chris@148 133
Chris@263 134 void
Chris@263 135 MP3FileReader::DecodeThread::run()
Chris@263 136 {
Chris@263 137 if (!m_reader->decode(m_reader->m_filebuffer, m_reader->m_fileSize)) {
Chris@263 138 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path);
Chris@263 139 }
Chris@263 140
Chris@263 141 delete[] m_reader->m_filebuffer;
Chris@263 142 m_reader->m_filebuffer = 0;
Chris@263 143
Chris@263 144 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@263 145
Chris@263 146 m_reader->m_done = true;
Chris@263 147 }
Chris@263 148
Chris@148 149 bool
Chris@148 150 MP3FileReader::decode(void *mm, size_t sz)
Chris@148 151 {
Chris@148 152 DecoderData data;
Chris@148 153 struct mad_decoder decoder;
Chris@148 154
Chris@148 155 data.start = (unsigned char const *)mm;
Chris@148 156 data.length = (unsigned long)sz;
Chris@148 157 data.reader = this;
Chris@148 158
Chris@148 159 mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0);
Chris@148 160 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
Chris@148 161 mad_decoder_finish(&decoder);
Chris@148 162
Chris@263 163 m_done = true;
Chris@148 164 return true;
Chris@148 165 }
Chris@148 166
Chris@148 167 enum mad_flow
Chris@148 168 MP3FileReader::input(void *dp, struct mad_stream *stream)
Chris@148 169 {
Chris@148 170 DecoderData *data = (DecoderData *)dp;
Chris@148 171
Chris@148 172 if (!data->length) return MAD_FLOW_STOP;
Chris@148 173 mad_stream_buffer(stream, data->start, data->length);
Chris@148 174 data->length = 0;
Chris@148 175
Chris@148 176 return MAD_FLOW_CONTINUE;
Chris@148 177 }
Chris@148 178
Chris@148 179 enum mad_flow
Chris@148 180 MP3FileReader::output(void *dp,
Chris@148 181 struct mad_header const *header,
Chris@148 182 struct mad_pcm *pcm)
Chris@148 183 {
Chris@148 184 DecoderData *data = (DecoderData *)dp;
Chris@148 185 return data->reader->accept(header, pcm);
Chris@148 186 }
Chris@148 187
Chris@148 188 enum mad_flow
Chris@148 189 MP3FileReader::accept(struct mad_header const *header,
Chris@148 190 struct mad_pcm *pcm)
Chris@148 191 {
Chris@148 192 int channels = pcm->channels;
Chris@148 193 int frames = pcm->length;
Chris@148 194
Chris@148 195 if (header) {
Chris@148 196 m_bitrateNum += header->bitrate;
Chris@148 197 m_bitrateDenom ++;
Chris@148 198 }
Chris@148 199
Chris@148 200 if (frames < 1) return MAD_FLOW_CONTINUE;
Chris@148 201
Chris@148 202 if (m_channelCount == 0) {
Chris@148 203 m_channelCount = channels;
Chris@148 204 m_sampleRate = pcm->samplerate;
Chris@148 205 }
Chris@148 206
Chris@148 207 if (m_bitrateDenom > 0) {
Chris@148 208 double bitrate = m_bitrateNum / m_bitrateDenom;
Chris@148 209 double duration = double(m_fileSize * 8) / bitrate;
Chris@148 210 double elapsed = double(m_frameCount) / m_sampleRate;
Chris@148 211 double percent = ((elapsed * 100.0) / duration);
Chris@263 212 if (m_progress) {
Chris@263 213 int progress = int(percent);
Chris@263 214 if (progress < 1) progress = 1;
Chris@263 215 if (progress > 99) progress = 99;
Chris@263 216 if (progress > m_progress->value()) {
Chris@263 217 m_progress->setValue(progress);
Chris@263 218 m_progress->show();
Chris@263 219 m_progress->raise();
Chris@263 220 qApp->processEvents();
Chris@263 221 if (m_progress->wasCanceled()) {
Chris@263 222 m_cancelled = true;
Chris@263 223 }
Chris@148 224 }
Chris@148 225 }
Chris@148 226 }
Chris@148 227
Chris@148 228 if (m_cancelled) return MAD_FLOW_STOP;
Chris@148 229
Chris@148 230 m_frameCount += frames;
Chris@148 231
Chris@148 232 if (!isDecodeCacheInitialised()) {
Chris@148 233 initialiseDecodeCache();
Chris@148 234 }
Chris@148 235
Chris@148 236 for (int i = 0; i < frames; ++i) {
Chris@148 237
Chris@148 238 for (int ch = 0; ch < channels; ++ch) {
Chris@148 239 mad_fixed_t sample = 0;
Chris@148 240 if (ch < int(sizeof(pcm->samples) / sizeof(pcm->samples[0]))) {
Chris@148 241 sample = pcm->samples[ch][i];
Chris@148 242 }
Chris@148 243 float fsample = float(sample) / float(MAD_F_ONE);
Chris@148 244 addSampleToDecodeCache(fsample);
Chris@148 245 }
Chris@148 246
Chris@148 247 if (! (i & 0xffff)) {
Chris@148 248 // periodically munlock to ensure we don't exhaust real memory
Chris@148 249 // if running with memory locked down
Chris@148 250 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 251 }
Chris@148 252 }
Chris@148 253
Chris@148 254 if (frames > 0) {
Chris@148 255 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 256 }
Chris@148 257
Chris@148 258 return MAD_FLOW_CONTINUE;
Chris@148 259 }
Chris@148 260
Chris@148 261 enum mad_flow
Chris@148 262 MP3FileReader::error(void *dp,
Chris@148 263 struct mad_stream *stream,
Chris@148 264 struct mad_frame *)
Chris@148 265 {
Chris@148 266 DecoderData *data = (DecoderData *)dp;
Chris@148 267
Chris@148 268 fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
Chris@148 269 stream->error, mad_stream_errorstr(stream),
Chris@148 270 stream->this_frame - data->start);
Chris@148 271
Chris@148 272 return MAD_FLOW_CONTINUE;
Chris@148 273 }
Chris@148 274
Chris@157 275 void
Chris@157 276 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 277 {
Chris@157 278 extensions.insert("mp3");
Chris@157 279 }
Chris@157 280
Chris@148 281 #endif