comparison data/fileio/MP3FileReader.cpp @ 0:fc9323a41f5a

start base : Sonic Visualiser sv1-1.0rc1
author lbajardsilogic
date Fri, 11 May 2007 09:08:14 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:fc9323a41f5a
1
2 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
3
4 /*
5 Sonic Visualiser
6 An audio file viewer and annotation editor.
7 Centre for Digital Music, Queen Mary, University of London.
8 This file copyright 2006 Chris Cannam.
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2 of the
13 License, or (at your option) any later version. See the file
14 COPYING included with this distribution for more information.
15 */
16
17 #ifdef HAVE_MAD
18
19 #include "MP3FileReader.h"
20 #include "system/System.h"
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25
26 #include <iostream>
27
28 #include <QApplication>
29 #include <QFileInfo>
30 #include <QProgressDialog>
31
32 MP3FileReader::MP3FileReader(QString path, bool showProgress, CacheMode mode) :
33 CodedAudioFileReader(mode),
34 m_path(path)
35 {
36 m_frameCount = 0;
37 m_channelCount = 0;
38 m_sampleRate = 0;
39 m_fileSize = 0;
40 m_bitrateNum = 0;
41 m_bitrateDenom = 0;
42 m_frameCount = 0;
43 m_cancelled = false;
44
45 struct stat stat;
46 if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
47 m_error = QString("File %1 does not exist.").arg(path);
48 return;
49 }
50
51 m_fileSize = stat.st_size;
52
53 int fd = -1;
54 if ((fd = ::open(path.toLocal8Bit().data(), O_RDONLY
55 #ifdef _WIN32
56 | O_BINARY
57 #endif
58 , 0)) < 0) {
59 m_error = QString("Failed to open file %1 for reading.").arg(path);
60 return;
61 }
62
63 unsigned char *filebuffer = 0;
64
65 try {
66 filebuffer = new unsigned char[m_fileSize];
67 } catch (...) {
68 m_error = QString("Out of memory");
69 ::close(fd);
70 return;
71 }
72
73 ssize_t sz = 0;
74 size_t offset = 0;
75 while (offset < m_fileSize) {
76 sz = ::read(fd, filebuffer + offset, m_fileSize - offset);
77 if (sz < 0) {
78 m_error = QString("Read error for file %1 (after %2 bytes)")
79 .arg(path).arg(offset);
80 delete[] filebuffer;
81 ::close(fd);
82 return;
83 } else if (sz == 0) {
84 std::cerr << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
85 .arg(offset).arg(m_fileSize).toStdString() << std::endl;
86 m_fileSize = offset;
87 break;
88 }
89 offset += sz;
90 }
91
92 ::close(fd);
93
94 if (showProgress) {
95 m_progress = new QProgressDialog
96 (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
97 QObject::tr("Stop"), 0, 100);
98 m_progress->hide();
99 }
100
101 if (!decode(filebuffer, m_fileSize)) {
102 m_error = QString("Failed to decode file %1.").arg(path);
103 delete[] filebuffer;
104 return;
105 }
106
107 if (isDecodeCacheInitialised()) finishDecodeCache();
108
109 if (showProgress) {
110 delete m_progress;
111 m_progress = 0;
112 }
113
114 delete[] filebuffer;
115 }
116
117 MP3FileReader::~MP3FileReader()
118 {
119 }
120
121 bool
122 MP3FileReader::decode(void *mm, size_t sz)
123 {
124 DecoderData data;
125 struct mad_decoder decoder;
126
127 data.start = (unsigned char const *)mm;
128 data.length = (unsigned long)sz;
129 data.reader = this;
130
131 mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0);
132 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
133 mad_decoder_finish(&decoder);
134
135 return true;
136 }
137
138 enum mad_flow
139 MP3FileReader::input(void *dp, struct mad_stream *stream)
140 {
141 DecoderData *data = (DecoderData *)dp;
142
143 if (!data->length) return MAD_FLOW_STOP;
144 mad_stream_buffer(stream, data->start, data->length);
145 data->length = 0;
146
147 return MAD_FLOW_CONTINUE;
148 }
149
150 enum mad_flow
151 MP3FileReader::output(void *dp,
152 struct mad_header const *header,
153 struct mad_pcm *pcm)
154 {
155 DecoderData *data = (DecoderData *)dp;
156 return data->reader->accept(header, pcm);
157 }
158
159 enum mad_flow
160 MP3FileReader::accept(struct mad_header const *header,
161 struct mad_pcm *pcm)
162 {
163 int channels = pcm->channels;
164 int frames = pcm->length;
165
166 if (header) {
167 m_bitrateNum += header->bitrate;
168 m_bitrateDenom ++;
169 }
170
171 if (frames < 1) return MAD_FLOW_CONTINUE;
172
173 if (m_channelCount == 0) {
174 m_channelCount = channels;
175 m_sampleRate = pcm->samplerate;
176 }
177
178 if (m_bitrateDenom > 0) {
179 double bitrate = m_bitrateNum / m_bitrateDenom;
180 double duration = double(m_fileSize * 8) / bitrate;
181 double elapsed = double(m_frameCount) / m_sampleRate;
182 double percent = ((elapsed * 100.0) / duration);
183 int progress = int(percent);
184 if (progress < 1) progress = 1;
185 if (progress > 99) progress = 99;
186 if (progress > m_progress->value()) {
187 m_progress->setValue(progress);
188 m_progress->show();
189 m_progress->raise();
190 qApp->processEvents();
191 if (m_progress->wasCanceled()) {
192 m_cancelled = true;
193 }
194 }
195 }
196
197 if (m_cancelled) return MAD_FLOW_STOP;
198
199 m_frameCount += frames;
200
201 if (!isDecodeCacheInitialised()) {
202 initialiseDecodeCache();
203 }
204
205 for (int i = 0; i < frames; ++i) {
206
207 for (int ch = 0; ch < channels; ++ch) {
208 mad_fixed_t sample = 0;
209 if (ch < int(sizeof(pcm->samples) / sizeof(pcm->samples[0]))) {
210 sample = pcm->samples[ch][i];
211 }
212 float fsample = float(sample) / float(MAD_F_ONE);
213 addSampleToDecodeCache(fsample);
214 }
215
216 if (! (i & 0xffff)) {
217 // periodically munlock to ensure we don't exhaust real memory
218 // if running with memory locked down
219 MUNLOCK_SAMPLEBLOCK(m_data);
220 }
221 }
222
223 if (frames > 0) {
224 MUNLOCK_SAMPLEBLOCK(m_data);
225 }
226
227 return MAD_FLOW_CONTINUE;
228 }
229
230 enum mad_flow
231 MP3FileReader::error(void *dp,
232 struct mad_stream *stream,
233 struct mad_frame *)
234 {
235 DecoderData *data = (DecoderData *)dp;
236
237 fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
238 stream->error, mad_stream_errorstr(stream),
239 stream->this_frame - data->start);
240
241 return MAD_FLOW_CONTINUE;
242 }
243
244 void
245 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
246 {
247 extensions.insert("mp3");
248 }
249
250 #endif