Mercurial > hg > svcore
diff data/fileio/MP3FileReader.cpp @ 1305:9f9f55a8af92 mp3-gapless
Add gapless flag to MP3FileReader, and implement trimming the delay samples from the start (padding is not yet trimmed from end)
author | Chris Cannam |
---|---|
date | Tue, 29 Nov 2016 11:35:56 +0000 |
parents | fa574c909c3d |
children | b325e91505b5 |
line wrap: on
line diff
--- a/data/fileio/MP3FileReader.cpp Tue Nov 29 08:58:50 2016 +0000 +++ b/data/fileio/MP3FileReader.cpp Tue Nov 29 11:35:56 2016 +0000 @@ -39,13 +39,19 @@ #define open _open #endif +using std::string; + +static sv_frame_t DEFAULT_DECODER_DELAY = 529; + MP3FileReader::MP3FileReader(FileSource source, DecodeMode decodeMode, - CacheMode mode, sv_samplerate_t targetRate, + CacheMode mode, GaplessMode gaplessMode, + sv_samplerate_t targetRate, bool normalised, ProgressReporter *reporter) : CodedAudioFileReader(mode, targetRate, normalised), m_source(source), m_path(source.getLocalFilename()), + m_gaplessMode(gaplessMode), m_decodeErrorShown(false), m_decodeThread(0) { @@ -65,6 +71,10 @@ m_done = false; m_reporter = reporter; + if (m_gaplessMode == Gapless) { + CodedAudioFileReader::setSamplesToTrim(DEFAULT_DECODER_DELAY, 0); + } + struct stat stat; if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) { m_error = QString("File %1 does not exist.").arg(m_path); @@ -365,26 +375,92 @@ return data->reader->filter(stream, frame); } +static string toMagic(uint32_t fourcc) +{ + string magic("...."); + for (int i = 0; i < 4; ++i) { + magic[3-i] = char((fourcc >> (8*i)) & 0xff); + } + return magic; +} + enum mad_flow MP3FileReader::filter(struct mad_stream const *stream, struct mad_frame *) { if (m_mp3FrameCount > 0) { + // only handle info frame if it appears as first mp3 frame return MAD_FLOW_CONTINUE; + } + + if (m_gaplessMode == Gappy) { + // Our non-gapless mode does not even filter out the Xing/LAME + // frame. That's because the main reason non-gapless mode + // exists is for backward compatibility with MP3FileReader + // behaviour before the gapless support was added, so we even + // need to keep the spurious 1152 samples resulting from + // feeding Xing/LAME frame to the decoder as otherwise we'd + // have different output from before. + SVDEBUG << "MP3FileReader: Not gapless mode, not checking Xing/LAME frame" + << endl; + return MAD_FLOW_CONTINUE; + } + + struct mad_bitptr ptr = stream->anc_ptr; + string magic = toMagic(mad_bit_read(&ptr, 32)); + + if (magic == "Xing" || magic == "Info") { + + SVDEBUG << "MP3FileReader: Found Xing/LAME metadata frame (magic = \"" + << magic << "\")" << endl; + + // All we want at this point is the LAME encoder delay and + // padding values. We expect to see the Xing/Info magic (which + // we've already read), then 116 bytes of Xing data, then LAME + // magic, 5 byte version string, 12 bytes of LAME data that we + // aren't currently interested in, then the delays encoded as + // two 12-bit numbers into three bytes. + // + // (See gabriel.mp3-tech.org/mp3infotag.html) + + for (int skip = 0; skip < 116; ++skip) { + (void)mad_bit_read(&ptr, 8); + } + + magic = toMagic(mad_bit_read(&ptr, 32)); + + if (magic == "LAME") { + + SVDEBUG << "MP3FileReader: Found LAME-specific metadata" << endl; + + for (int skip = 0; skip < 5 + 12; ++skip) { + (void)mad_bit_read(&ptr, 8); + } + + uint32_t delay = mad_bit_read(&ptr, 12); + uint32_t padding = mad_bit_read(&ptr, 12); + + sv_frame_t delayToDrop = DEFAULT_DECODER_DELAY + delay; + sv_frame_t paddingToDrop = padding - DEFAULT_DECODER_DELAY; + if (paddingToDrop < 0) paddingToDrop = 0; + + SVDEBUG << "MP3FileReader: LAME encoder delay = " << delay + << ", padding = " << padding << endl; + + SVDEBUG << "MP3FileReader: Will be trimming " << delayToDrop + << " samples at start and " << paddingToDrop + << " at end" << endl; + + CodedAudioFileReader::setSamplesToTrim(delayToDrop, paddingToDrop); + + } else { + SVDEBUG << "MP3FileReader: Xing frame has no LAME metadata" << endl; + } + + return MAD_FLOW_IGNORE; + } else { - struct mad_bitptr ptr = stream->anc_ptr; - unsigned long fourcc = mad_bit_read(&ptr, 32); - std::string magic("...."); - for (int i = 0; i < 4; ++i) { - magic[3-i] = char((fourcc >> (8*i)) & 0xff); - } - if (magic == "Xing" || magic == "Info" || magic == "LAME") { - SVDEBUG << "MP3FileReader: Discarding metadata frame (magic = \"" - << magic << "\")" << endl; - return MAD_FLOW_IGNORE; - } else { - return MAD_FLOW_CONTINUE; - } + return MAD_FLOW_CONTINUE; } }