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;
     }
 }