shithub: sox

Download patch

ref: 25982e4ff28ba2d92131db29adcd2b940b2f6bad
parent: c7709bad17ca756694a68aed2696f993ed9056ce
author: cbagwell <cbagwell>
date: Sun Jan 9 11:20:55 EST 2011

add seek support which must read samples but can skip unneeded
decoding and should be faster.  Patch from Pavel Karneliuk.

--- a/ChangeLog
+++ b/ChangeLog
@@ -13,6 +13,7 @@
 
   o Fix regression where MP3 handler required libmad headers to be installed.
     (Samuli Suominen) 
+  o Add seek support to mp3 handler for speed improvements.  (Pavel Karneliuk)
 
 Audio device drivers:
 
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -524,6 +524,110 @@
   LSX_DLLIBRARY_CLOSE(p, mad_dl);
   return SOX_SUCCESS;
 }
+
+static int sox_mp3seek(sox_format_t * ft, uint64_t offset)
+{
+  priv_t   * p = (priv_t *) ft->priv;
+  size_t   initial_bitrate = p->Frame.header.bitrate;
+  size_t   tagsize = 0, consumed = 0;
+  sox_bool vbr = sox_false; /* Variable Bit Rate */
+  sox_bool depadded = sox_false;
+  uint64_t to_skip_samples = 0;
+
+  /* Reset all */
+  rewind(ft->fp);
+  mad_timer_reset(&p->Timer);
+  p->FrameCount = 0;
+
+  /* They where opened in startread */
+  mad_synth_finish(&p->Synth);
+  p->mad_frame_finish(&p->Frame);
+  p->mad_stream_finish(&p->Stream);
+
+  p->mad_stream_init(&p->Stream);
+  p->mad_frame_init(&p->Frame);
+  p->mad_synth_init(&p->Synth);
+
+  offset /= ft->signal.channels;
+  to_skip_samples = offset;
+
+  while(sox_true) {  /* Read data from the MP3 file */
+    int read, padding = 0;
+    size_t leftover = p->Stream.bufend - p->Stream.next_frame;
+
+    memcpy(p->mp3_buffer, p->Stream.this_frame, leftover);
+    read = fread(p->mp3_buffer + leftover, (size_t) 1, p->mp3_buffer_size - leftover, ft->fp);
+    if (read <= 0) {
+      lsx_debug("seek failure. unexpected EOF (frames=%lu leftover=%lu)", (unsigned long)p->FrameCount, (unsigned long)leftover);
+      break;
+    }
+    for (; !depadded && padding < read && !p->mp3_buffer[padding]; ++padding);
+    depadded = sox_true;
+    p->mad_stream_buffer(&p->Stream, p->mp3_buffer + padding, leftover + read - padding);
+
+    while (to_skip_samples > 0) {  /* Decode frame headers */
+      static unsigned short samples;
+      p->Stream.error = MAD_ERROR_NONE;
+      
+      /* Not an audio frame */
+      if (p->mad_header_decode(&p->Frame.header, &p->Stream) == -1) {
+        if (p->Stream.error == MAD_ERROR_BUFLEN)
+          break;  /* Normal behaviour; get some more data from the file */
+        if (!MAD_RECOVERABLE(p->Stream.error)) {
+          lsx_warn("unrecoverable MAD error");
+          break;
+        }
+        if (p->Stream.error == MAD_ERROR_LOSTSYNC) {
+          unsigned available = (p->Stream.bufend - p->Stream.this_frame);
+          tagsize = tagtype(p->Stream.this_frame, (size_t) available);
+          if (tagsize) {   /* It's some ID3 tags, so just skip */
+            if (tagsize >= available) {
+              fseeko(ft->fp, (off_t)(tagsize - available), SEEK_CUR);
+              depadded = sox_false;
+            }
+            p->mad_stream_skip(&p->Stream, min(tagsize, available));
+          }
+          else lsx_warn("MAD lost sync");
+        }
+        else lsx_warn("recoverable MAD error");
+        continue;
+      }
+
+      consumed += p->Stream.next_frame - p->Stream.this_frame;
+      vbr      |= (p->Frame.header.bitrate != initial_bitrate);
+
+      samples = 32 * MAD_NSBSAMPLES(&p->Frame.header);
+
+      p->FrameCount++;
+      p->mad_timer_add(&p->Timer, p->Frame.header.duration);
+
+      if(to_skip_samples <= samples)
+      {
+        p->mad_frame_decode(&p->Frame,&p->Stream);
+        p->mad_synth_frame(&p->Synth, &p->Frame);
+        p->cursamp = to_skip_samples;
+        return SOX_SUCCESS;
+      }
+      else to_skip_samples -= samples;
+
+      /* If not VBR, we can extrapolate frame size */
+      if (p->FrameCount == 64 && !vbr) {
+        p->FrameCount = offset / samples;
+        to_skip_samples = offset % samples;
+
+        if (SOX_SUCCESS != lsx_seeki(ft, (p->FrameCount * consumed / 64) + tagsize, SEEK_SET))
+          return SOX_EOF;
+
+        /* Reset Stream for refilling buffer */
+        p->mad_stream_finish(&p->Stream);
+        p->mad_stream_init(&p->Stream);
+        break;
+      }
+    }
+  };
+
+  return SOX_EOF;
+}
 #else /*HAVE_MAD_H*/
 static int startread(sox_format_t * ft)
 {
@@ -532,6 +636,7 @@
 }
 #define sox_mp3read NULL
 #define stopread NULL
+#define sox_mp3seek NULL
 #endif /*HAVE_MAD_H*/
 
 #ifdef HAVE_LAME
@@ -1014,7 +1119,7 @@
     "MPEG Layer 3 lossy audio compression", names, 0,
     startread, sox_mp3read, stopread,
     startwrite, sox_mp3write, stopwrite,
-    NULL, write_encodings, NULL, sizeof(priv_t)
+    sox_mp3seek, write_encodings, NULL, sizeof(priv_t)
   };
   return &handler;
 }