svcore  1.9
MP3FileReader.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006 Chris Cannam.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #ifdef HAVE_MAD
17 
18 #include "MP3FileReader.h"
19 #include "base/ProgressReporter.h"
20 
21 #include "system/System.h"
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 
27 #include <iostream>
28 
29 #include <cstdlib>
30 #include <unistd.h>
31 
32 #ifdef HAVE_ID3TAG
33 #include <id3tag.h>
34 #endif
35 //#define DEBUG_ID3TAG 1
36 
37 #include <QFileInfo>
38 
40  CacheMode mode, int targetRate,
41  bool normalised,
42  ProgressReporter *reporter) :
43  CodedAudioFileReader(mode, targetRate, normalised),
44  m_source(source),
45  m_path(source.getLocalFilename()),
46  m_decodeThread(0)
47 {
48  m_channelCount = 0;
49  m_fileRate = 0;
50  m_fileSize = 0;
51  m_bitrateNum = 0;
52  m_bitrateDenom = 0;
53  m_cancelled = false;
54  m_completion = 0;
55  m_done = false;
56  m_reporter = reporter;
57 
58  struct stat stat;
59  if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
60  m_error = QString("File %1 does not exist.").arg(m_path);
61  return;
62  }
63 
64  m_fileSize = stat.st_size;
65 
66  m_filebuffer = 0;
67  m_samplebuffer = 0;
69 
70  int fd = -1;
71  if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY
72 #ifdef _WIN32
73  | O_BINARY
74 #endif
75  , 0)) < 0) {
76  m_error = QString("Failed to open file %1 for reading.").arg(m_path);
77  return;
78  }
79 
80  try {
81  m_filebuffer = new unsigned char[m_fileSize];
82  } catch (...) {
83  m_error = QString("Out of memory");
84  ::close(fd);
85  return;
86  }
87 
88  ssize_t sz = 0;
89  int offset = 0;
90  while (offset < m_fileSize) {
91  sz = ::read(fd, m_filebuffer + offset, m_fileSize - offset);
92  if (sz < 0) {
93  m_error = QString("Read error for file %1 (after %2 bytes)")
94  .arg(m_path).arg(offset);
95  delete[] m_filebuffer;
96  ::close(fd);
97  return;
98  } else if (sz == 0) {
99  cerr << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
100  .arg(offset).arg(m_fileSize) << endl;
101  m_fileSize = offset;
102  break;
103  }
104  offset += sz;
105  }
106 
107  ::close(fd);
108 
109  loadTags();
110 
111  if (decodeMode == DecodeAtOnce) {
112 
113  if (m_reporter) {
114  connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
116  (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
117  }
118 
119  if (!decode(m_filebuffer, m_fileSize)) {
120  m_error = QString("Failed to decode file %1.").arg(m_path);
121  }
122 
123  delete[] m_filebuffer;
124  m_filebuffer = 0;
125 
127  endSerialised();
128 
129  } else {
130 
131  if (m_reporter) m_reporter->setProgress(100);
132 
133  m_decodeThread = new DecodeThread(this);
135 
136  while ((m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0)
137  && !m_done) {
138  usleep(10);
139  }
140 
141  cerr << "MP3FileReader ctor: exiting with file rate = " << m_fileRate << endl;
142  }
143 
144  if (m_error != "") {
145  cerr << "MP3FileReader::MP3FileReader(\"" << m_path << "\"): ERROR: " << m_error << endl;
146  }
147 }
148 
150 {
151  if (m_decodeThread) {
152  m_cancelled = true;
153  m_decodeThread->wait();
154  delete m_decodeThread;
155  }
156 }
157 
158 void
160 {
161  m_cancelled = true;
162 }
163 
164 void
166 {
167  m_title = "";
168 
169 #ifdef HAVE_ID3TAG
170 
171  id3_file *file = id3_file_open(m_path.toLocal8Bit().data(),
172  ID3_FILE_MODE_READONLY);
173  if (!file) return;
174 
175  // We can do this a lot more elegantly, but we'll leave that for
176  // when we implement support for more than just the one tag!
177 
178  id3_tag *tag = id3_file_tag(file);
179  if (!tag) {
180 #ifdef DEBUG_ID3TAG
181  SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl;
182 #endif
183  id3_file_close(file);
184  return;
185  }
186 
187  m_title = loadTag(tag, "TIT2"); // work title
188  if (m_title == "") m_title = loadTag(tag, "TIT1");
189 
190  m_maker = loadTag(tag, "TPE1"); // "lead artist"
191  if (m_maker == "") m_maker = loadTag(tag, "TPE2");
192 
193  for (unsigned int i = 0; i < tag->nframes; ++i) {
194  if (tag->frames[i]) {
195  QString value = loadTag(tag, tag->frames[i]->id);
196  if (value != "") m_tags[tag->frames[i]->id] = value;
197  }
198  }
199 
200  id3_file_close(file);
201 
202 #else
203 #ifdef DEBUG_ID3TAG
204  SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in"
205  << endl;
206 #endif
207 #endif
208 }
209 
210 QString
211 MP3FileReader::loadTag(void *vtag, const char *name)
212 {
213 #ifdef HAVE_ID3TAG
214  id3_tag *tag = (id3_tag *)vtag;
215 
216  id3_frame *frame = id3_tag_findframe(tag, name, 0);
217  if (!frame) {
218 #ifdef DEBUG_ID3TAG
219  SVDEBUG << "MP3FileReader::loadTags: No \"" << name << "\" in ID3 tag" << endl;
220 #endif
221  return "";
222  }
223 
224  if (frame->nfields < 2) {
225  SVDEBUG << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << endl;
226  return "";
227  }
228 
229  unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
230  if (nstrings == 0) {
231 #ifdef DEBUG_ID3TAG
232  SVDEBUG << "MP3FileReader::loadTags: No strings for \"" << name << "\" in ID3 tag" << endl;
233 #endif
234  return "";
235  }
236 
237  id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
238  if (!ustr) {
239 #ifdef DEBUG_ID3TAG
240  SVDEBUG << "MP3FileReader::loadTags: Invalid or absent data for \"" << name << "\" in ID3 tag" << endl;
241 #endif
242  return "";
243  }
244 
245  id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
246  if (!u8str) {
247  cerr << "MP3FileReader::loadTags: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 title" << endl;
248  return "";
249  }
250 
251  QString rv = QString::fromUtf8((const char *)u8str);
252  free(u8str);
253 
254 #ifdef DEBUG_ID3TAG
255  SVDEBUG << "MP3FileReader::loadTags: tag \"" << name << "\" -> \""
256  << rv << "\"" << endl;
257 #endif
258 
259 
260  return rv;
261 
262 #else
263  return "";
264 #endif
265 }
266 
267 void
269 {
271  m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path);
272  }
273 
274  delete[] m_reader->m_filebuffer;
275  m_reader->m_filebuffer = 0;
276 
277  if (m_reader->m_samplebuffer) {
278  for (int c = 0; c < m_reader->m_channelCount; ++c) {
279  delete[] m_reader->m_samplebuffer[c];
280  }
281  delete[] m_reader->m_samplebuffer;
283  }
284 
286 
287  m_reader->m_done = true;
288  m_reader->m_completion = 100;
289 
291 }
292 
293 bool
294 MP3FileReader::decode(void *mm, int sz)
295 {
296  DecoderData data;
297  struct mad_decoder decoder;
298 
299  data.start = (unsigned char const *)mm;
300  data.length = (unsigned long)sz;
301  data.reader = this;
302 
303  mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0);
304  mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
305  mad_decoder_finish(&decoder);
306 
307  m_done = true;
308  return true;
309 }
310 
311 enum mad_flow
312 MP3FileReader::input(void *dp, struct mad_stream *stream)
313 {
314  DecoderData *data = (DecoderData *)dp;
315 
316  if (!data->length) return MAD_FLOW_STOP;
317 
318  unsigned char const *start = data->start;
319  unsigned long length = data->length;
320 
321 #ifdef HAVE_ID3TAG
322  if (length > ID3_TAG_QUERYSIZE) {
323  int taglen = id3_tag_query(start, ID3_TAG_QUERYSIZE);
324  if (taglen > 0) {
325 // cerr << "ID3 tag length to skip: " << taglen << endl;
326  start += taglen;
327  length -= taglen;
328  }
329  }
330 #endif
331 
332  mad_stream_buffer(stream, start, length);
333  data->length = 0;
334 
335  return MAD_FLOW_CONTINUE;
336 }
337 
338 enum mad_flow
340  struct mad_header const *header,
341  struct mad_pcm *pcm)
342 {
343  DecoderData *data = (DecoderData *)dp;
344  return data->reader->accept(header, pcm);
345 }
346 
347 enum mad_flow
348 MP3FileReader::accept(struct mad_header const *header,
349  struct mad_pcm *pcm)
350 {
351  int channels = pcm->channels;
352  int frames = pcm->length;
353 
354  if (header) {
355  m_bitrateNum += header->bitrate;
356  m_bitrateDenom ++;
357  }
358 
359  if (frames < 1) return MAD_FLOW_CONTINUE;
360 
361  if (m_channelCount == 0) {
362 
363  m_fileRate = pcm->samplerate;
364  m_channelCount = channels;
365 
367 
369 // SVDEBUG << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << endl;
370  startSerialised("MP3FileReader::Decode");
371  }
372  }
373 
374  if (m_bitrateDenom > 0) {
375  double bitrate = m_bitrateNum / m_bitrateDenom;
376  double duration = double(m_fileSize * 8) / bitrate;
377  double elapsed = double(m_frameCount) / m_sampleRate;
378  double percent = 100;
379  if (duration > 0.0) percent = ((elapsed * 100.0) / duration);
380  int p = int(percent);
381  if (p < 1) p = 1;
382  if (p > 99) p = 99;
383  if (m_completion != p && m_reporter) {
384  m_completion = p;
386  }
387  }
388 
389  if (m_cancelled) return MAD_FLOW_STOP;
390 
391  if (!isDecodeCacheInitialised()) {
393  }
394 
395  if (int(m_samplebuffersize) < frames) {
396  if (!m_samplebuffer) {
397  m_samplebuffer = new float *[channels];
398  for (int c = 0; c < channels; ++c) {
399  m_samplebuffer[c] = 0;
400  }
401  }
402  for (int c = 0; c < channels; ++c) {
403  delete[] m_samplebuffer[c];
404  m_samplebuffer[c] = new float[frames];
405  }
406  m_samplebuffersize = frames;
407  }
408 
409  int activeChannels = int(sizeof(pcm->samples) / sizeof(pcm->samples[0]));
410 
411  for (int ch = 0; ch < channels; ++ch) {
412 
413  for (int i = 0; i < frames; ++i) {
414 
415  mad_fixed_t sample = 0;
416  if (ch < activeChannels) {
417  sample = pcm->samples[ch][i];
418  }
419  float fsample = float(sample) / float(MAD_F_ONE);
420 
421  m_samplebuffer[ch][i] = fsample;
422  }
423  }
424 
426 
427  return MAD_FLOW_CONTINUE;
428 }
429 
430 enum mad_flow
431 MP3FileReader::error(void * /* dp */,
432  struct mad_stream * /* stream */,
433  struct mad_frame *)
434 {
435 // DecoderData *data = (DecoderData *)dp;
436 
437 // fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %lu\n",
438 // stream->error, mad_stream_errorstr(stream),
439 // (unsigned long)(stream->this_frame - data->start));
440 
441  return MAD_FLOW_CONTINUE;
442 }
443 
444 void
445 MP3FileReader::getSupportedExtensions(std::set<QString> &extensions)
446 {
447  extensions.insert("mp3");
448 }
449 
450 bool
452 {
453  std::set<QString> extensions;
454  getSupportedExtensions(extensions);
455  return (extensions.find(extension.toLower()) != extensions.end());
456 }
457 
458 bool
460 {
461  return (type == "audio/mpeg");
462 }
463 
464 bool
466 {
467  return (supportsExtension(source.getExtension()) ||
469 }
470 
471 
472 #endif
QString getExtension() const
Return the file extension for this file, if any.
Definition: FileSource.cpp:619
unsigned char const * start
Definition: MP3FileReader.h:91
QString loadTag(void *vtag, const char *name)
static void getSupportedExtensions(std::set< QString > &extensions)
static enum mad_flow input(void *, struct mad_stream *)
void start()
Definition: Thread.cpp:34
DecodeThread * m_decodeThread
enum mad_flow accept(struct mad_header const *, struct mad_pcm *)
static enum mad_flow output(void *, struct mad_header const *, struct mad_pcm *)
QString m_error
Definition: MP3FileReader.h:72
void addSamplesToDecodeCache(float **samples, int nframes)
QString m_maker
Definition: MP3FileReader.h:74
unsigned char * m_filebuffer
Definition: MP3FileReader.h:82
float ** m_samplebuffer
Definition: MP3FileReader.h:83
MP3FileReader(FileSource source, DecodeMode decodeMode, CacheMode cacheMode, int targetRate=0, bool normalised=false, ProgressReporter *reporter=0)
int m_samplebuffersize
Definition: MP3FileReader.h:84
QString m_path
Definition: MP3FileReader.h:71
FileSource is a class used to refer to the contents of a file that may be either local or at a remote...
Definition: FileSource.h:59
double m_bitrateNum
Definition: MP3FileReader.h:77
bool decode(void *mm, int sz)
virtual void setProgress(int percentage)=0
static bool supports(FileSource &source)
#define SVDEBUG
Definition: Debug.h:42
bool isDecodeCacheInitialised() const
ProgressReporter * m_reporter
Definition: MP3FileReader.h:86
QString getContentType() const
Return the MIME content type of this file, if known.
Definition: FileSource.cpp:613
virtual void setMessage(QString text)=0
static enum mad_flow error(void *, struct mad_stream *, struct mad_frame *)
QString m_title
Definition: MP3FileReader.h:73
void startSerialised(QString id)
static bool supportsExtension(QString ext)
virtual ~MP3FileReader()
static bool supportsContentType(QString type)