annotate data/fileio/MP3FileReader.cpp @ 167:665342c6ec57

* Add a bit of resistance to pane dragging so as to make it harder to inadvertently drag in the other axis from the one you intended
author Chris Cannam
date Fri, 22 Sep 2006 16:46:10 +0000
parents c03ec31005e1
children 06ad01f3e553
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@148 29 #include <QProgressDialog>
Chris@148 30
Chris@148 31 MP3FileReader::MP3FileReader(QString path, bool showProgress, CacheMode mode) :
Chris@148 32 CodedAudioFileReader(mode),
Chris@148 33 m_path(path)
Chris@148 34 {
Chris@148 35 m_frameCount = 0;
Chris@148 36 m_channelCount = 0;
Chris@148 37 m_sampleRate = 0;
Chris@148 38 m_fileSize = 0;
Chris@148 39 m_bitrateNum = 0;
Chris@148 40 m_bitrateDenom = 0;
Chris@148 41 m_frameCount = 0;
Chris@148 42 m_cancelled = false;
Chris@148 43
Chris@148 44 struct stat stat;
Chris@148 45 if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
Chris@148 46 m_error = QString("File %1 does not exist.").arg(path);
Chris@148 47 return;
Chris@148 48 }
Chris@148 49
Chris@148 50 m_fileSize = stat.st_size;
Chris@148 51
Chris@148 52 int fd;
Chris@148 53 if ((fd = ::open(path.toLocal8Bit().data(), O_RDONLY, 0)) < 0) {
Chris@148 54 m_error = QString("Failed to open file %1 for reading.").arg(path);
Chris@148 55 return;
Chris@148 56 }
Chris@148 57
Chris@148 58 unsigned char *filebuffer = 0;
Chris@148 59
Chris@148 60 try {
Chris@148 61 filebuffer = new unsigned char[stat.st_size];
Chris@148 62 } catch (...) {
Chris@148 63 m_error = QString("Out of memory");
Chris@148 64 ::close(fd);
Chris@148 65 return;
Chris@148 66 }
Chris@148 67
Chris@148 68 if (::read(fd, filebuffer, stat.st_size) < stat.st_size) {
Chris@148 69 m_error = QString("Failed to read file %1.").arg(path);
Chris@148 70 delete[] filebuffer;
Chris@148 71 ::close(fd);
Chris@148 72 return;
Chris@148 73 }
Chris@148 74
Chris@148 75 ::close(fd);
Chris@148 76
Chris@148 77 if (showProgress) {
Chris@148 78 m_progress = new QProgressDialog
Chris@148 79 (QObject::tr("Decoding MP3 file..."),
Chris@148 80 QObject::tr("Stop"), 0, 100);
Chris@148 81 m_progress->hide();
Chris@148 82 }
Chris@148 83
Chris@148 84 if (!decode(filebuffer, stat.st_size)) {
Chris@148 85 m_error = QString("Failed to decode file %1.").arg(path);
Chris@148 86 delete[] filebuffer;
Chris@148 87 return;
Chris@148 88 }
Chris@148 89
Chris@148 90 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@148 91
Chris@148 92 if (showProgress) {
Chris@148 93 delete m_progress;
Chris@148 94 m_progress = 0;
Chris@148 95 }
Chris@148 96
Chris@148 97 delete[] filebuffer;
Chris@148 98 }
Chris@148 99
Chris@148 100 MP3FileReader::~MP3FileReader()
Chris@148 101 {
Chris@148 102 }
Chris@148 103
Chris@148 104 bool
Chris@148 105 MP3FileReader::decode(void *mm, size_t sz)
Chris@148 106 {
Chris@148 107 DecoderData data;
Chris@148 108 struct mad_decoder decoder;
Chris@148 109
Chris@148 110 data.start = (unsigned char const *)mm;
Chris@148 111 data.length = (unsigned long)sz;
Chris@148 112 data.reader = this;
Chris@148 113
Chris@148 114 mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0);
Chris@148 115 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
Chris@148 116 mad_decoder_finish(&decoder);
Chris@148 117
Chris@148 118 return true;
Chris@148 119 }
Chris@148 120
Chris@148 121 enum mad_flow
Chris@148 122 MP3FileReader::input(void *dp, struct mad_stream *stream)
Chris@148 123 {
Chris@148 124 DecoderData *data = (DecoderData *)dp;
Chris@148 125
Chris@148 126 if (!data->length) return MAD_FLOW_STOP;
Chris@148 127 mad_stream_buffer(stream, data->start, data->length);
Chris@148 128 data->length = 0;
Chris@148 129
Chris@148 130 return MAD_FLOW_CONTINUE;
Chris@148 131 }
Chris@148 132
Chris@148 133 enum mad_flow
Chris@148 134 MP3FileReader::output(void *dp,
Chris@148 135 struct mad_header const *header,
Chris@148 136 struct mad_pcm *pcm)
Chris@148 137 {
Chris@148 138 DecoderData *data = (DecoderData *)dp;
Chris@148 139 return data->reader->accept(header, pcm);
Chris@148 140 }
Chris@148 141
Chris@148 142 enum mad_flow
Chris@148 143 MP3FileReader::accept(struct mad_header const *header,
Chris@148 144 struct mad_pcm *pcm)
Chris@148 145 {
Chris@148 146 int channels = pcm->channels;
Chris@148 147 int frames = pcm->length;
Chris@148 148
Chris@148 149 if (header) {
Chris@148 150 m_bitrateNum += header->bitrate;
Chris@148 151 m_bitrateDenom ++;
Chris@148 152 }
Chris@148 153
Chris@148 154 if (frames < 1) return MAD_FLOW_CONTINUE;
Chris@148 155
Chris@148 156 if (m_channelCount == 0) {
Chris@148 157 m_channelCount = channels;
Chris@148 158 m_sampleRate = pcm->samplerate;
Chris@148 159 }
Chris@148 160
Chris@148 161 if (m_bitrateDenom > 0) {
Chris@148 162 double bitrate = m_bitrateNum / m_bitrateDenom;
Chris@148 163 double duration = double(m_fileSize * 8) / bitrate;
Chris@148 164 double elapsed = double(m_frameCount) / m_sampleRate;
Chris@148 165 double percent = ((elapsed * 100.0) / duration);
Chris@148 166 int progress = int(percent);
Chris@148 167 if (progress < 1) progress = 1;
Chris@148 168 if (progress > 99) progress = 99;
Chris@148 169 if (progress > m_progress->value()) {
Chris@148 170 m_progress->setValue(progress);
Chris@148 171 m_progress->show();
Chris@148 172 m_progress->raise();
Chris@148 173 qApp->processEvents();
Chris@148 174 if (m_progress->wasCanceled()) {
Chris@148 175 m_cancelled = true;
Chris@148 176 }
Chris@148 177 }
Chris@148 178 }
Chris@148 179
Chris@148 180 if (m_cancelled) return MAD_FLOW_STOP;
Chris@148 181
Chris@148 182 m_frameCount += frames;
Chris@148 183
Chris@148 184 if (!isDecodeCacheInitialised()) {
Chris@148 185 initialiseDecodeCache();
Chris@148 186 }
Chris@148 187
Chris@148 188 for (int i = 0; i < frames; ++i) {
Chris@148 189
Chris@148 190 for (int ch = 0; ch < channels; ++ch) {
Chris@148 191 mad_fixed_t sample = 0;
Chris@148 192 if (ch < int(sizeof(pcm->samples) / sizeof(pcm->samples[0]))) {
Chris@148 193 sample = pcm->samples[ch][i];
Chris@148 194 }
Chris@148 195 float fsample = float(sample) / float(MAD_F_ONE);
Chris@148 196 addSampleToDecodeCache(fsample);
Chris@148 197 }
Chris@148 198
Chris@148 199 if (! (i & 0xffff)) {
Chris@148 200 // periodically munlock to ensure we don't exhaust real memory
Chris@148 201 // if running with memory locked down
Chris@148 202 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 203 }
Chris@148 204 }
Chris@148 205
Chris@148 206 if (frames > 0) {
Chris@148 207 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 208 }
Chris@148 209
Chris@148 210 return MAD_FLOW_CONTINUE;
Chris@148 211 }
Chris@148 212
Chris@148 213 enum mad_flow
Chris@148 214 MP3FileReader::error(void *dp,
Chris@148 215 struct mad_stream *stream,
Chris@148 216 struct mad_frame *)
Chris@148 217 {
Chris@148 218 DecoderData *data = (DecoderData *)dp;
Chris@148 219
Chris@148 220 fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
Chris@148 221 stream->error, mad_stream_errorstr(stream),
Chris@148 222 stream->this_frame - data->start);
Chris@148 223
Chris@148 224 return MAD_FLOW_CONTINUE;
Chris@148 225 }
Chris@148 226
Chris@157 227 void
Chris@157 228 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 229 {
Chris@157 230 extensions.insert("mp3");
Chris@157 231 }
Chris@157 232
Chris@148 233 #endif