24 #include <sys/types.h> 59 m_path(source.getLocalFilename()),
60 m_gaplessMode(gaplessMode),
61 m_decodeErrorShown(false),
62 m_decodeThread(nullptr)
65 <<
"\", decode mode: " << decodeMode <<
" (" 66 << (decodeMode ==
DecodeAtOnce ?
"DecodeAtOnce" :
"DecodeThreaded")
93 if (!qfile.open(QIODevice::ReadOnly)) {
94 m_error = QString(
"Failed to open file %1 for reading.").arg(
m_path);
105 SVDEBUG <<
"file size = " <<
m_fileSize <<
", buffer guard = " << MAD_BUFFER_GUARD << endl;
110 m_error = QString(
"Out of memory");
115 auto amountRead = qfile.read(reinterpret_cast<char *>(
m_fileBuffer),
119 SVCERR << QString(
"MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes")
134 (tr(
"Decoding %1...").arg(QFileInfo(
m_path).fileName()));
167 SVDEBUG <<
"MP3FileReader: decoding startup complete, file rate = " <<
m_fileRate << endl;
177 Profiler profiler(
"MP3FileReader::~MP3FileReader");
200 int id3fd = _dup(fd);
205 id3_file *file = id3_file_fdopen(id3fd, ID3_FILE_MODE_READONLY);
211 id3_tag *tag = id3_file_tag(file);
213 SVDEBUG <<
"MP3FileReader::loadTags: No ID3 tag found" << endl;
214 id3_file_close(file);
220 if (
m_title ==
"")
SVDEBUG <<
"MP3FileReader::loadTags: No title found" << endl;
224 if (
m_maker ==
"")
SVDEBUG <<
"MP3FileReader::loadTags: No artist/maker found" << endl;
226 for (
unsigned int i = 0; i < tag->nframes; ++i) {
227 if (tag->frames[i]) {
228 QString value =
loadTag(tag, tag->frames[i]->id);
230 m_tags[tag->frames[i]->id] = value;
235 id3_file_close(file);
238 SVDEBUG <<
"MP3FileReader::loadTags: ID3 tag support not compiled in" << endl;
246 id3_tag *tag = (id3_tag *)vtag;
248 id3_frame *frame = id3_tag_findframe(tag, name, 0);
250 SVDEBUG <<
"MP3FileReader::loadTag: No \"" << name <<
"\" frame found in ID3 tag" << endl;
254 if (frame->nfields < 2) {
255 cerr <<
"MP3FileReader::loadTag: WARNING: Not enough fields (" << frame->nfields <<
") for \"" << name <<
"\" in ID3 tag" << endl;
259 unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
261 SVDEBUG <<
"MP3FileReader::loadTag: No strings for \"" << name <<
"\" in ID3 tag" << endl;
265 id3_ucs4_t
const *ustr = id3_field_getstrings(&frame->fields[1], 0);
267 SVDEBUG <<
"MP3FileReader::loadTag: Invalid or absent data for \"" << name <<
"\" in ID3 tag" << endl;
271 id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
273 SVDEBUG <<
"MP3FileReader::loadTag: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 tag" << endl;
277 QString rv = QString::fromUtf8((
const char *)u8str);
280 SVDEBUG <<
"MP3FileReader::loadTag: Tag \"" << name <<
"\" -> \"" 281 << rv <<
"\"" << endl;
293 if (!m_reader->decode(m_reader->m_fileBuffer, m_reader->m_fileBufferSize)) {
294 m_reader->m_error = QString(
"Failed to decode file %1.").arg(m_reader->m_path);
297 delete[] m_reader->m_fileBuffer;
298 m_reader->m_fileBuffer =
nullptr;
300 if (m_reader->m_sampleBuffer) {
301 for (
int c = 0; c < m_reader->m_channelCount; ++c) {
302 delete[] m_reader->m_sampleBuffer[c];
304 delete[] m_reader->m_sampleBuffer;
305 m_reader->m_sampleBuffer =
nullptr;
308 if (m_reader->isDecodeCacheInitialised()) {
309 m_reader->finishDecodeCache();
312 m_reader->m_done =
true;
313 m_reader->m_completion = 100;
315 m_reader->endSerialised();
322 struct mad_decoder decoder;
324 data.
start = (
unsigned char const *)mm;
329 mad_decoder_init(&decoder,
338 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
339 mad_decoder_finish(&decoder);
342 <<
" mp3 frames" << endl;
355 return MAD_FLOW_STOP;
358 unsigned char const *start = data->
start;
362 while (length > ID3_TAG_QUERYSIZE) {
363 ssize_t taglen = id3_tag_query(start, ID3_TAG_QUERYSIZE);
367 SVDEBUG <<
"MP3FileReader: ID3 tag length to skip: " << taglen << endl;
373 mad_stream_buffer(stream, start, length);
376 return MAD_FLOW_CONTINUE;
381 struct mad_stream
const *stream,
382 struct mad_frame *frame)
390 string magic(
"....");
391 for (
int i = 0; i < 4; ++i) {
392 magic[3-i] = char((fourcc >> (8*i)) & 0xff);
403 return MAD_FLOW_CONTINUE;
414 SVDEBUG <<
"MP3FileReader: Not gapless mode, not checking Xing/LAME frame" 416 return MAD_FLOW_CONTINUE;
419 struct mad_bitptr ptr = stream->anc_ptr;
420 string magic =
toMagic(mad_bit_read(&ptr, 32));
422 if (magic ==
"Xing" || magic ==
"Info") {
424 SVDEBUG <<
"MP3FileReader: Found Xing/LAME metadata frame (magic = \"" 425 << magic <<
"\")" << endl;
436 for (
int skip = 0; skip < 116; ++skip) {
437 (void)mad_bit_read(&ptr, 8);
440 magic =
toMagic(mad_bit_read(&ptr, 32));
442 if (magic ==
"LAME") {
444 SVDEBUG <<
"MP3FileReader: Found LAME-specific metadata" << endl;
446 for (
int skip = 0; skip < 5 + 12; ++skip) {
447 (void)mad_bit_read(&ptr, 8);
450 auto delay = mad_bit_read(&ptr, 12);
451 auto padding = mad_bit_read(&ptr, 12);
455 if (paddingToDrop < 0) paddingToDrop = 0;
457 SVDEBUG <<
"MP3FileReader: LAME encoder delay = " << delay
458 <<
", padding = " << padding << endl;
460 SVDEBUG <<
"MP3FileReader: Will be trimming " << delayToDrop
461 <<
" samples from start and " << paddingToDrop
462 <<
" from end" << endl;
467 SVDEBUG <<
"MP3FileReader: Xing frame has no LAME metadata" << endl;
470 return MAD_FLOW_IGNORE;
473 return MAD_FLOW_CONTINUE;
479 struct mad_header
const *header,
490 int channels = pcm->channels;
491 int frames = pcm->length;
498 if (frames < 1)
return MAD_FLOW_CONTINUE;
505 SVDEBUG <<
"MP3FileReader::accept: file rate = " << pcm->samplerate
506 <<
", channel count = " << channels <<
", about to init " 507 <<
"decode cache" << endl;
515 return MAD_FLOW_STOP;
522 double duration = double(
m_fileSize * 8) / bitrate;
524 double percent = 100;
525 if (duration > 0.0) percent = ((elapsed * 100.0) / duration);
526 int p = int(percent);
536 SVDEBUG <<
"MP3FileReader: Decoding cancelled" << endl;
537 return MAD_FLOW_STOP;
541 SVDEBUG <<
"MP3FileReader::accept: fallback case: file rate = " << pcm->samplerate
542 <<
", channel count = " << channels <<
", about to init " 543 <<
"decode cache" << endl;
550 for (
int c = 0; c < channels; ++c) {
554 for (
int c = 0; c < channels; ++c) {
561 int activeChannels = int(
sizeof(pcm->samples) /
sizeof(pcm->samples[0]));
563 for (
int ch = 0; ch < channels; ++ch) {
565 for (
int i = 0; i < frames; ++i) {
567 mad_fixed_t sample = 0;
568 if (ch < activeChannels) {
569 sample = pcm->samples[ch][i];
571 float fsample = float(sample) / float(MAD_F_ONE);
581 return MAD_FLOW_CONTINUE;
586 struct mad_stream *stream,
593 if (stream->error == MAD_ERROR_LOSTSYNC &&
597 return MAD_FLOW_CONTINUE;
602 snprintf(buffer, 255,
603 "MP3 decoding error 0x%04x (%s) at byte offset %lld",
604 stream->error, mad_stream_errorstr(stream), (
long long int)ix);
606 << buffer <<
" (continuing; will not report any further decode errors for this file)" << endl;
610 return MAD_FLOW_CONTINUE;
616 extensions.insert(
"mp3");
622 std::set<QString> extensions;
624 return (extensions.find(extension.toLower()) != extensions.end());
630 return (type ==
"audio/mpeg");
double sv_samplerate_t
Sample rate.
unsigned char const * start
QString loadTag(void *vtag, const char *name)
enum mad_flow filter(struct mad_stream const *, struct mad_frame *)
int64_t sv_frame_t
Frame index, the unit of our time axis.
static void getSupportedExtensions(std::set< QString > &extensions)
GaplessMode
How the MP3FileReader should handle leading and trailing gaps.
static sv_frame_t DEFAULT_DECODER_DELAY
DecodeThread * m_decodeThread
unsigned char * m_fileBuffer
size_t m_sampleBufferSize
GaplessMode m_gaplessMode
enum mad_flow accept(struct mad_header const *, struct mad_pcm *)
static enum mad_flow filter_callback(void *, struct mad_stream const *, struct mad_frame *)
void startSerialised(QString id, const std::atomic< bool > *cancelled)
static string toMagic(unsigned long fourcc)
MP3FileReader(FileSource source, DecodeMode decodeMode, CacheMode cacheMode, GaplessMode gaplessMode, sv_samplerate_t targetRate=0, bool normalised=false, ProgressReporter *reporter=0)
Trim unwanted samples from the start and end of the decoded audio.
bool decode(void *mm, sv_frame_t sz)
FileSource is a class used to refer to the contents of a file that may be either local or at a remote...
void addSamplesToDecodeCache(float **samples, sv_frame_t nframes)
QString getContentType() const
Return the MIME content type of this file, if known.
sv_samplerate_t m_fileRate
static enum mad_flow output_callback(void *, struct mad_header const *, struct mad_pcm *)
virtual void setProgress(int percentage)=0
static bool supports(FileSource &source)
ProgressReporter * m_reporter
void setFramesToTrim(sv_frame_t fromStart, sv_frame_t fromEnd)
void initialiseDecodeCache()
virtual void setMessage(QString text)=0
QString getExtension() const
Return the file extension for this file, if any.
static enum mad_flow error_callback(void *, struct mad_stream *, struct mad_frame *)
std::atomic< bool > m_cancelled
sv_samplerate_t m_sampleRate
bool isDecodeCacheInitialised() const
static bool supportsExtension(QString ext)
static bool supportsContentType(QString type)
static enum mad_flow input_callback(void *, struct mad_stream *)
Profile point instance class.