shithub: sox

Download patch

ref: 9b1f7f515d0173a16a263f2d16a0b3a541530177
parent: 586dec6fe8c26f1422b110f9e74b31e98bc9ef2e
author: robs <robs>
date: Sun Sep 27 11:43:13 EDT 2009

Refactor dynamic loading, fix MP3 issues - ID: 2859256 - latest patch

--- a/src/mp3-util.h
+++ b/src/mp3-util.h
@@ -30,7 +30,7 @@
   {NULL, NULL}
 };
 
-#if defined(HAVE_LAME_LAME_H) && defined(HAVE_ID3TAG_SET_FIELDVALUE)
+#if defined(HAVE_LAME)
 
 static void write_comments(sox_format_t * ft)
 {
@@ -41,6 +41,9 @@
   const char* comment;
   size_t required_size;
 
+  p->id3tag_init(p->gfp);
+  p->id3tag_set_pad(p->gfp, ID3PADDING);
+
   for (i = 0; id3tagmap[i][0]; i++)
   {
     comment = sox_find_comment(ft->oob.comments, id3tagmap[i][1]);
@@ -69,7 +72,7 @@
   free(id3tag_buf);
 }
 
-#endif /* HAVE_LAME_LAME_H && HAVE_ID3TAG_SET_FIELDVALUE */
+#endif /* HAVE_LAME */
 
 #ifdef USING_ID3TAG
 
@@ -133,11 +136,11 @@
 
 static void mad_timer_mult(mad_timer_t * t, double d)
 {
-  t->seconds = d *= (t->seconds + t->fraction * (1. / MAD_TIMER_RESOLUTION));
-  t->fraction = (d - t->seconds) * MAD_TIMER_RESOLUTION + .5;
+  t->seconds = (signed long)(d *= (t->seconds + t->fraction * (1. / MAD_TIMER_RESOLUTION)));
+  t->fraction = (unsigned long)((d - t->seconds) * MAD_TIMER_RESOLUTION + .5);
 }
 
-static size_t mp3_duration_ms(sox_format_t * ft, unsigned char *buffer)
+static size_t mp3_duration_ms(sox_format_t * ft)
 {
   priv_t              * p = (priv_t *) ft->priv;
   FILE                * fp = ft->fp;
@@ -157,15 +160,15 @@
     int read, padding = 0;
     size_t leftover = mad_stream.bufend - mad_stream.next_frame;
 
-    memcpy(buffer, mad_stream.this_frame, leftover);
-    read = fread(buffer + leftover, (size_t) 1, INPUT_BUFFER_SIZE - leftover, fp);
+    memcpy(p->mp3_buffer, mad_stream.this_frame, leftover);
+    read = fread(p->mp3_buffer + leftover, (size_t) 1, p->mp3_buffer_size - leftover, fp);
     if (read <= 0) {
       lsx_debug("got exact duration by scan to EOF (frames=%lu leftover=%lu)", (unsigned long)frames, (unsigned long)leftover);
       break;
     }
-    for (; !depadded && padding < read && !buffer[padding]; ++padding);
+    for (; !depadded && padding < read && !p->mp3_buffer[padding]; ++padding);
     depadded = sox_true;
-    p->mad_stream_buffer(&mad_stream, buffer + padding, leftover + read - padding);
+    p->mad_stream_buffer(&mad_stream, p->mp3_buffer + padding, leftover + read - padding);
 
     while (sox_true) {  /* Decode frame headers */
       mad_stream.error = MAD_ERROR_NONE;
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -10,17 +10,24 @@
  */
 
 #include "sox_i.h"
+#include <string.h>
 
-#if defined(HAVE_LAME_LAME_H) || defined (HAVE_MAD_H)
+#if defined(HAVE_LAME_LAME_H) || defined(HAVE_LAME_H)
+#define HAVE_LAME 1
+#else
+#undef HAVE_LAME
+#endif
 
-#include <string.h>
+#if defined(HAVE_MAD_H) || defined(HAVE_LAME)
 
 #ifdef HAVE_MAD_H
 #include <mad.h>
 #endif
 
-#ifdef HAVE_LAME_LAME_H
+#if defined(HAVE_LAME_LAME_H)
 #include <lame/lame.h>
+#elif defined(HAVE_LAME_H)
+#include <lame.h>
 #endif
 
 #if defined(HAVE_ID3TAG) && (defined(HAVE_IO_H) || defined(HAVE_UNISTD_H))
@@ -38,89 +45,139 @@
   #define ID3_TAG_FLAG_FOOTERPRESENT 0x10
 #endif
 
-#if defined HAVE_LIBLTDL
-  #include <ltdl.h>
-#if defined HAVE_MAD_H && defined DL_MAD
-mad_timer_t const mad_timer_zero;
+#ifndef HAVE_LIBLTDL
+  #undef DL_LAME
+  #undef DL_MAD
 #endif
+
+/* Under Windows, importing data from DLLs is a dicey proposition. This is true
+ * when using dlopen, but also true if linking directly against the DLL if the
+ * header does not mark the data as __declspec(dllexport), which mad.h does not.
+ * Sidestep the issue by defining our own mad_timer_zero. This is needed because
+ * mad_timer_zero is used in some of the mad.h macros.
+ */
+#define mad_timer_zero mad_timer_zero_stub
+static mad_timer_t const mad_timer_zero_stub = {0, 0};
+
+#define MAXFRAMESIZE 2880
+#define ID3PADDING 128
+
+static const char* const mad_library_names[] =
+{
+#ifdef DL_MAD
+    "libmad",
+    "cygmad-0",
 #endif
+    NULL
+};
 
-#define INPUT_BUFFER_SIZE       (sox_globals.bufsiz)
+#ifdef DL_MAD
+  #define MAD_FUNC LSX_DLENTRY_DYNAMIC
+#else
+  #define MAD_FUNC LSX_DLENTRY_STATIC
+#endif
 
+#define MAD_FUNC_ENTRIES(f,x) \
+  MAD_FUNC(f,x, void, mad_stream_buffer, (struct mad_stream *, unsigned char const *, unsigned long)) \
+  MAD_FUNC(f,x, void, mad_stream_skip, (struct mad_stream *, unsigned long)) \
+  MAD_FUNC(f,x, int, mad_stream_sync, (struct mad_stream *)) \
+  MAD_FUNC(f,x, void, mad_stream_init, (struct mad_stream *)) \
+  MAD_FUNC(f,x, void, mad_frame_init, (struct mad_frame *)) \
+  MAD_FUNC(f,x, void, mad_synth_init, (struct mad_synth *)) \
+  MAD_FUNC(f,x, int, mad_frame_decode, (struct mad_frame *, struct mad_stream *)) \
+  MAD_FUNC(f,x, void, mad_timer_add, (mad_timer_t *, mad_timer_t)) \
+  MAD_FUNC(f,x, void, mad_synth_frame, (struct mad_synth *, struct mad_frame const *)) \
+  MAD_FUNC(f,x, char const *, mad_stream_errorstr, (struct mad_stream const *)) \
+  MAD_FUNC(f,x, void, mad_frame_finish, (struct mad_frame *)) \
+  MAD_FUNC(f,x, void, mad_stream_finish, (struct mad_stream *)) \
+  MAD_FUNC(f,x, unsigned long, mad_bit_read, (struct mad_bitptr *, unsigned int)) \
+  MAD_FUNC(f,x, int, mad_header_decode, (struct mad_header *, struct mad_stream *)) \
+  MAD_FUNC(f,x, void, mad_header_init, (struct mad_header *)) \
+  MAD_FUNC(f,x, signed long, mad_timer_count, (mad_timer_t, enum mad_units)) \
+  MAD_FUNC(f,x, void, mad_timer_multiply, (mad_timer_t *, signed long))
+
+static const char* const lame_library_names[] =
+{
+#ifdef DL_LAME
+  "libmp3lame",
+  "lame-enc",
+#endif
+  NULL
+};
+
+#ifdef DL_LAME
+  #define LAME_FUNC           LSX_DLENTRY_DYNAMIC
+  #define LAME_FUNC_MSG       LSX_DLENTRY_STUB
+  #define LAME_FUNC_LAMETAG   LSX_DLENTRY_STUB
+  #define LAME_FUNC_ID3       LSX_DLENTRY_STUB
+#else
+  #define LAME_FUNC           LSX_DLENTRY_STATIC
+  #ifdef HAVE_LAME_SET_MSGF
+    #define LAME_FUNC_MSG     LSX_DLENTRY_STATIC
+  #else
+    #define LAME_FUNC_MSG     LSX_DLENTRY_STUB
+  #endif
+  #ifdef HAVE_LAME_GET_LAMETAG_FRAME
+    #define LAME_FUNC_LAMETAG LSX_DLENTRY_STATIC
+  #else
+    #define LAME_FUNC_LAMETAG LSX_DLENTRY_STUB
+  #endif
+  #ifdef HAVE_LAME_ID3TAG
+    #define LAME_FUNC_ID3     LSX_DLENTRY_STATIC
+  #else
+    #define LAME_FUNC_ID3     LSX_DLENTRY_STUB
+  #endif
+#endif
+
+#define LAME_FUNC_ENTRIES(f,x) \
+  LAME_FUNC(f,x, lame_global_flags*, lame_init, (void)) \
+  LAME_FUNC_MSG(f,x, int, lame_set_errorf, (lame_global_flags *, void (*)(const char *, va_list))) \
+  LAME_FUNC_MSG(f,x, int, lame_set_debugf, (lame_global_flags *, void (*)(const char *, va_list))) \
+  LAME_FUNC_MSG(f,x, int, lame_set_msgf, (lame_global_flags *, void (*)(const char *, va_list))) \
+  LAME_FUNC(f,x, int, lame_set_num_samples, (lame_global_flags *, unsigned long)) \
+  LAME_FUNC(f,x, int, lame_get_num_channels, (const lame_global_flags *)) \
+  LAME_FUNC(f,x, int, lame_set_num_channels, (lame_global_flags *, int)) \
+  LAME_FUNC(f,x, int, lame_set_in_samplerate, (lame_global_flags *, int)) \
+  LAME_FUNC(f,x, int, lame_set_out_samplerate, (lame_global_flags *, int)) \
+  LAME_FUNC(f,x, int, lame_set_bWriteVbrTag, (lame_global_flags *, int)) \
+  LAME_FUNC(f,x, int, lame_set_brate, (lame_global_flags *, int)) \
+  LAME_FUNC(f,x, int, lame_set_quality, (lame_global_flags *, int)) \
+  LAME_FUNC(f,x, vbr_mode, lame_get_VBR, (const lame_global_flags *)) \
+  LAME_FUNC(f,x, int, lame_set_VBR, (lame_global_flags *, vbr_mode)) \
+  LAME_FUNC(f,x, int, lame_set_VBR_q, (lame_global_flags *, int)) \
+  LAME_FUNC(f,x, int, lame_init_params, (lame_global_flags *)) \
+  LAME_FUNC(f,x, int, lame_encode_buffer, (lame_global_flags *, const short int[], const short int[], const int, unsigned char *, const int)) \
+  LAME_FUNC(f,x, int, lame_encode_flush, (lame_global_flags *, unsigned char *, int)) \
+  LAME_FUNC(f,x, int, lame_close, (lame_global_flags *)) \
+  LAME_FUNC_LAMETAG(f,x, size_t, lame_get_lametag_frame, (const lame_global_flags *, unsigned char*, size_t)) \
+  LAME_FUNC_ID3(f,x, size_t, lame_get_id3v2_tag, (lame_global_flags *, unsigned char*, size_t)) \
+  LAME_FUNC_ID3(f,x, void, id3tag_init, (lame_global_flags *)) \
+  LAME_FUNC_ID3(f,x, void, id3tag_set_pad, (lame_global_flags *, size_t)) \
+  LAME_FUNC_ID3(f,x, int, id3tag_set_fieldvalue, (lame_global_flags *, const char *))
+
 /* Private data */
-typedef struct {
+typedef struct mp3_priv_t {
+  unsigned char *mp3_buffer;
+  size_t mp3_buffer_size;
+
 #ifdef HAVE_MAD_H
   struct mad_stream       Stream;
   struct mad_frame        Frame;
   struct mad_synth        Synth;
   mad_timer_t             Timer;
-  unsigned char           *InputBuffer;
   ptrdiff_t               cursamp;
   size_t                  FrameCount;
-
-  void (*mad_stream_buffer)(struct mad_stream *, unsigned char const *,
-                            unsigned long);
-  void (*mad_stream_skip)(struct mad_stream *, unsigned long);
-  int (*mad_stream_sync)(struct mad_stream *);
-  void (*mad_stream_init)(struct mad_stream *);
-  void (*mad_frame_init)(struct mad_frame *);
-  void (*mad_synth_init)(struct mad_synth *);
-  int (*mad_frame_decode)(struct mad_frame *, struct mad_stream *);
-  void (*mad_timer_add)(mad_timer_t *, mad_timer_t);
-  void (*mad_synth_frame)(struct mad_synth *, struct mad_frame const *);
-  char const *(*mad_stream_errorstr)(struct mad_stream const *);
-  void (*mad_frame_finish)(struct mad_frame *);
-  void (*mad_stream_finish)(struct mad_stream *);
-  unsigned long (*mad_bit_read)(struct mad_bitptr *, unsigned int);
-  int (*mad_header_decode)(struct mad_header *, struct mad_stream *);
-  void (*mad_header_init)(struct mad_header *);
-  signed long (*mad_timer_count)(mad_timer_t, enum mad_units);
-  void (*mad_timer_multiply)(mad_timer_t *, signed long);
-  #if defined HAVE_LIBLTDL && defined DL_MAD
-  lt_dlhandle mad_lth;
-  #endif
+  LSX_DLENTRIES_TO_PTRS(MAD_FUNC_ENTRIES, mad_dl);
 #endif /*HAVE_MAD_H*/
 
-#ifdef HAVE_LAME_LAME_H
-  lame_global_flags       *gfp;
-
-  lame_global_flags * (*lame_init)(void);
-  int (*lame_set_num_channels)(lame_global_flags *, int);
-  int (*lame_get_num_channels)(const lame_global_flags *);
-  int (*lame_set_in_samplerate)(lame_global_flags *, int);
-  int (*lame_set_bWriteVbrTag)(lame_global_flags *, int);
-  int (*lame_get_bWriteVbrTag)(lame_global_flags const *);
-  #ifdef HAVE_ID3TAG_SET_FIELDVALUE
-  int (*id3tag_set_fieldvalue)(lame_global_flags *, const char *);
-  #endif
-  int (*lame_init_params)(lame_global_flags *);
-  int (*lame_set_errorf)(lame_global_flags *,
-                         void (*func)(const char *, va_list));
-  int (*lame_set_debugf)(lame_global_flags *,
-                         void (*func)(const char *, va_list));
-  int (*lame_set_msgf)(lame_global_flags *,
-                       void (*func)(const char *, va_list));
-  int (*lame_encode_buffer)(lame_global_flags *, const short int[],
-                            const short int[], const int,
-                            unsigned char *, const int);
-  int (*lame_encode_flush)(lame_global_flags *, unsigned char *,
-                           int);
-  int (*lame_close)(lame_global_flags *);
-  void (*lame_mp3_tags_fid)(lame_global_flags *, FILE *);
-  int (*lame_get_brate)(const lame_global_flags *);
-  int (*lame_set_brate)(lame_global_flags *, int);
-  int (*lame_set_quality)(lame_global_flags *, int);
-  int (*lame_set_VBR)(lame_global_flags *, vbr_mode);
-  int (*lame_set_VBR_min_bitrate_kbps)(lame_global_flags *, int);
-  #ifdef HAVE_LAME_SET_VBR_QUALITY
-  int (*lame_set_VBR_quality)(lame_global_flags *, float);
-  #endif
-  vbr_mode (*lame_get_VBR)(const lame_global_flags *);
-
-  #if defined HAVE_LIBLTDL && defined DL_LAME
-  lt_dlhandle lame_lth;
-  #endif
-#endif /*HAVE_LAME_LAME_H*/
+#ifdef HAVE_LAME
+  lame_global_flags *gfp;
+  short *pcm_buffer;
+  size_t pcm_buffer_size;
+  unsigned long num_samples;
+  int vbr_tag;
+  LSX_DLENTRIES_TO_PTRS(LAME_FUNC_ENTRIES, lame_dl);
+#endif /*HAVE_LAME*/
 } priv_t;
 
 /* This function merges the functions tagtype() and id3_tag_query()
@@ -179,16 +236,16 @@
      * TODO: Is 2016 bytes the size of the largest frame?
      * (448000*(1152/32000))/8
      */
-    memmove(p->InputBuffer, p->Stream.next_frame, remaining);
+    memmove(p->mp3_buffer, p->Stream.next_frame, remaining);
 
-    bytes_read = lsx_readbuf(ft, p->InputBuffer+remaining,
-                            INPUT_BUFFER_SIZE-remaining);
+    bytes_read = lsx_readbuf(ft, p->mp3_buffer+remaining,
+                            p->mp3_buffer_size-remaining);
     if (bytes_read == 0)
     {
         return SOX_EOF;
     }
 
-    p->mad_stream_buffer(&p->Stream, p->InputBuffer, bytes_read+remaining);
+    p->mad_stream_buffer(&p->Stream, p->mp3_buffer, bytes_read+remaining);
     p->Stream.error = 0;
 
     return SOX_SUCCESS;
@@ -238,141 +295,110 @@
   priv_t *p = (priv_t *) ft->priv;
   size_t ReadSize;
   sox_bool ignore_length = ft->signal.length == SOX_IGNORE_LENGTH;
+  int open_library_result;
 
-#if defined HAVE_LIBLTDL && defined DL_MAD
-  #define DL_LIB_NAME "MAD decoder library (libmad"
-  #define LOAD_FN_PTR(x) \
-    if (!(ltptr.ptr = lt_dlsym(p->mad_lth, #x))) { \
-      lsx_fail("incompatible " DL_LIB_NAME " is missing "#x")"); \
-      return SOX_EOF; \
-    } \
-    p->x = ltptr.fn;
-  union {void (* fn)(); lt_ptr ptr;} ltptr;
-  if (!lt_dlinit())
-    p->mad_lth = lt_dlopenext("libmad");
-  if (!p->mad_lth) {
-    lsx_fail("could not find " DL_LIB_NAME ")");
+  LSX_DLLIBRARY_OPEN(
+      p,
+      mad_dl,
+      MAD_FUNC_ENTRIES,
+      "MAD decoder library",
+      mad_library_names,
+      open_library_result);
+  if (open_library_result)
     return SOX_EOF;
-  }
-#else
-  #define DL_LIB_NAME
-  #define LOAD_FN_PTR(x) p->x = x;
-#endif
 
-  LOAD_FN_PTR(mad_bit_read)
-  LOAD_FN_PTR(mad_frame_decode)
-  LOAD_FN_PTR(mad_frame_finish)
-  LOAD_FN_PTR(mad_frame_init)
-  LOAD_FN_PTR(mad_header_decode)
-  LOAD_FN_PTR(mad_header_init)
-  LOAD_FN_PTR(mad_stream_buffer)
-  LOAD_FN_PTR(mad_stream_errorstr)
-  LOAD_FN_PTR(mad_stream_finish)
-  LOAD_FN_PTR(mad_stream_init)
-  LOAD_FN_PTR(mad_stream_skip)
-  LOAD_FN_PTR(mad_stream_sync)
-  LOAD_FN_PTR(mad_synth_frame)
-  LOAD_FN_PTR(mad_synth_init)
-  LOAD_FN_PTR(mad_timer_add)
-  LOAD_FN_PTR(mad_timer_count)
-  LOAD_FN_PTR(mad_timer_multiply)
+  p->mp3_buffer_size = sox_globals.bufsiz;
+  p->mp3_buffer = lsx_malloc(p->mp3_buffer_size);
 
-#undef LOAD_FN_PTR
-#undef DL_LIB_NAME
-
-    p->InputBuffer = NULL;
-
-    p->InputBuffer=lsx_malloc(INPUT_BUFFER_SIZE);
-
-    ft->signal.length = SOX_UNSPEC;
-    if (ft->seekable) {
+  ft->signal.length = SOX_UNSPEC;
+  if (ft->seekable) {
 #ifdef USING_ID3TAG
-      read_comments(ft);
-      rewind(ft->fp);
-      if (!ft->signal.length)
+    read_comments(ft);
+    rewind(ft->fp);
+    if (!ft->signal.length)
 #endif
-        if (!ignore_length)
-          ft->signal.length = mp3_duration_ms(ft, p->InputBuffer);
-    }
+      if (!ignore_length)
+        ft->signal.length = mp3_duration_ms(ft);
+  }
 
-    p->mad_stream_init(&p->Stream);
-    p->mad_frame_init(&p->Frame);
-    p->mad_synth_init(&p->Synth);
-    mad_timer_reset(&p->Timer);
+  p->mad_stream_init(&p->Stream);
+  p->mad_frame_init(&p->Frame);
+  p->mad_synth_init(&p->Synth);
+  mad_timer_reset(&p->Timer);
 
-    ft->encoding.encoding = SOX_ENCODING_MP3;
+  ft->encoding.encoding = SOX_ENCODING_MP3;
 
-    /* Decode at least one valid frame to find out the input
-     * format.  The decoded frame will be saved off so that it
-     * can be processed later.
-     */
-    ReadSize = lsx_readbuf(ft, p->InputBuffer, INPUT_BUFFER_SIZE);
-    if (ReadSize != INPUT_BUFFER_SIZE && ferror(ft->fp))
-      return SOX_EOF;
+  /* Decode at least one valid frame to find out the input
+   * format.  The decoded frame will be saved off so that it
+   * can be processed later.
+   */
+  ReadSize = lsx_readbuf(ft, p->mp3_buffer, p->mp3_buffer_size);
+  if (ReadSize != p->mp3_buffer_size && ferror(ft->fp))
+    return SOX_EOF;
 
-    p->mad_stream_buffer(&p->Stream, p->InputBuffer, ReadSize);
+  p->mad_stream_buffer(&p->Stream, p->mp3_buffer, ReadSize);
 
-    /* Find a valid frame before starting up.  This makes sure
-     * that we have a valid MP3 and also skips past ID3v2 tags
-     * at the beginning of the audio file.
-     */
-    p->Stream.error = 0;
-    while (p->mad_frame_decode(&p->Frame,&p->Stream))
-    {
-        /* check whether input buffer needs a refill */
-        if (p->Stream.error == MAD_ERROR_BUFLEN)
-        {
-            if (sox_mp3_input(ft) == SOX_EOF)
-                return SOX_EOF;
+  /* Find a valid frame before starting up.  This makes sure
+   * that we have a valid MP3 and also skips past ID3v2 tags
+   * at the beginning of the audio file.
+   */
+  p->Stream.error = 0;
+  while (p->mad_frame_decode(&p->Frame,&p->Stream))
+  {
+      /* check whether input buffer needs a refill */
+      if (p->Stream.error == MAD_ERROR_BUFLEN)
+      {
+          if (sox_mp3_input(ft) == SOX_EOF)
+              return SOX_EOF;
 
-            continue;
-        }
+          continue;
+      }
 
-        /* Consume any ID3 tags */
-        sox_mp3_inputtag(ft);
+      /* Consume any ID3 tags */
+      sox_mp3_inputtag(ft);
 
-        /* FIXME: We should probably detect when we've read
-         * a bunch of non-ID3 data and still haven't found a
-         * frame.  In that case we can abort early without
-         * scanning the whole file.
-         */
-        p->Stream.error = 0;
-    }
+      /* FIXME: We should probably detect when we've read
+       * a bunch of non-ID3 data and still haven't found a
+       * frame.  In that case we can abort early without
+       * scanning the whole file.
+       */
+      p->Stream.error = 0;
+  }
 
-    if (p->Stream.error)
-    {
-        lsx_fail_errno(ft,SOX_EOF,"No valid MP3 frame found");
-        return SOX_EOF;
-    }
-
-    switch(p->Frame.header.mode)
-    {
-        case MAD_MODE_SINGLE_CHANNEL:
-        case MAD_MODE_DUAL_CHANNEL:
-        case MAD_MODE_JOINT_STEREO:
-        case MAD_MODE_STEREO:
-            ft->signal.channels = MAD_NCHANNELS(&p->Frame.header);
-            break;
-        default:
-            lsx_fail_errno(ft, SOX_EFMT, "Cannot determine number of channels");
-            return SOX_EOF;
-    }
+  if (p->Stream.error)
+  {
+      lsx_fail_errno(ft,SOX_EOF,"No valid MP3 frame found");
+      return SOX_EOF;
+  }
 
-    p->FrameCount=1;
+  switch(p->Frame.header.mode)
+  {
+      case MAD_MODE_SINGLE_CHANNEL:
+      case MAD_MODE_DUAL_CHANNEL:
+      case MAD_MODE_JOINT_STEREO:
+      case MAD_MODE_STEREO:
+          ft->signal.channels = MAD_NCHANNELS(&p->Frame.header);
+          break;
+      default:
+          lsx_fail_errno(ft, SOX_EFMT, "Cannot determine number of channels");
+          return SOX_EOF;
+  }
 
-    p->mad_timer_add(&p->Timer,p->Frame.header.duration);
-    p->mad_synth_frame(&p->Synth,&p->Frame);
-    ft->signal.rate=p->Synth.pcm.samplerate;
-    if (ignore_length)
-      ft->signal.length = SOX_UNSPEC;
-    else {
-      ft->signal.length = ft->signal.length * .001 * ft->signal.rate + .5;
-      ft->signal.length *= ft->signal.channels;  /* Keep separate from line above! */
-    }
+  p->FrameCount=1;
 
-    p->cursamp = 0;
+  p->mad_timer_add(&p->Timer,p->Frame.header.duration);
+  p->mad_synth_frame(&p->Synth,&p->Frame);
+  ft->signal.rate=p->Synth.pcm.samplerate;
+  if (ignore_length)
+    ft->signal.length = SOX_UNSPEC;
+  else {
+    ft->signal.length = (size_t)(ft->signal.length * .001 * ft->signal.rate + .5);
+    ft->signal.length *= ft->signal.channels;  /* Keep separate from line above! */
+  }
 
-    return SOX_SUCCESS;
+  p->cursamp = 0;
+
+  return SOX_SUCCESS;
 }
 
 /*
@@ -455,11 +481,8 @@
   p->mad_frame_finish(&p->Frame);
   p->mad_stream_finish(&p->Stream);
 
-  free(p->InputBuffer);
-#if defined HAVE_LIBLTDL && defined DL_MAD
-  if (!lt_dlclose(p->mad_lth))
-    lt_dlexit();
-#endif
+  free(p->mp3_buffer);
+  LSX_DLLIBRARY_CLOSE(p, mad_dl);
   return SOX_SUCCESS;
 }
 #else /*HAVE_MAD_H*/
@@ -472,7 +495,7 @@
 #define stopread NULL
 #endif /*HAVE_MAD_H*/
 
-#ifdef HAVE_LAME_LAME_H
+#ifdef HAVE_LAME
 
 /* Adapters for lame message callbacks: */
 
@@ -500,60 +523,171 @@
   return;
 }
 
-static int startwrite(sox_format_t * ft)
+/* These functions are considered optional. If they aren't present in the
+   library, the stub versions defined here will be used instead. */
+
+static int lame_set_errorf_stub(lame_global_flags * gfp UNUSED, void (*func)(const char *, va_list) UNUSED)
+  { return 0; }
+static int lame_set_debugf_stub(lame_global_flags * gfp UNUSED, void (*func)(const char *, va_list) UNUSED)
+  { return 0; }
+static int lame_set_msgf_stub(lame_global_flags * gfp UNUSED, void (*func)(const char *, va_list) UNUSED)
+  { return 0; }
+static size_t lame_get_lametag_frame_stub(const lame_global_flags * gfp UNUSED, unsigned char * buffer UNUSED, size_t size UNUSED)
+  { return 0; }
+static size_t lame_get_id3v2_tag_stub(lame_global_flags * gfp UNUSED, unsigned char * buffer UNUSED, size_t size UNUSED)
+  { return 0; }
+static void id3tag_init_stub(lame_global_flags * gfp UNUSED)
+  { return; }
+static void id3tag_set_pad_stub(lame_global_flags * gfp UNUSED, size_t padding UNUSED)
+  { return; }
+static int id3tag_set_fieldvalue_stub(lame_global_flags * gfp UNUSED, const char *fieldvalue UNUSED)
+  { return 0; }
+
+static int get_id3v2_tag_size(sox_format_t * ft)
 {
-  priv_t *p = (priv_t *) ft->priv;
+  priv_t *p = (priv_t *)ft->priv;
+  FILE *fp = ft->fp;
+  size_t bytes_read;
+  int id3v2_size;
+  unsigned char id3v2_header[10];
 
-#if defined HAVE_LIBLTDL && defined DL_LAME
-  #define DL_LIB_NAME "LAME encoder library (libmp3lame"
-  #define LOAD_FN_PTR(x) \
-    if (!(ltptr.ptr = lt_dlsym(p->lame_lth, #x))) { \
-      lsx_fail("incompatible " DL_LIB_NAME " is missing "#x")"); \
-      return SOX_EOF; \
-    } \
-    p->x = ltptr.fn;
-  union {int (* fn)(); lt_ptr ptr;} ltptr;
-  if (!lt_dlinit())
-    p->lame_lth = lt_dlopenext("libmp3lame");
-  if (!p->lame_lth) {
-    lsx_fail("could not find " DL_LIB_NAME ")");
+  if (fseeko(fp, 0, SEEK_SET) != 0) {
+    lsx_warn("cannot update id3 tag - failed to seek to beginning");
     return SOX_EOF;
   }
-#else
-  #define DL_LIB_NAME
-  #define LOAD_FN_PTR(x) p->x = x;
-#endif
 
-  LOAD_FN_PTR(lame_init)
-  LOAD_FN_PTR(lame_set_num_channels)
-  LOAD_FN_PTR(lame_get_num_channels)
-  LOAD_FN_PTR(lame_set_in_samplerate)
-  LOAD_FN_PTR(lame_set_bWriteVbrTag)
-  LOAD_FN_PTR(lame_get_bWriteVbrTag)
-#ifdef HAVE_ID3TAG_SET_FIELDVALUE
-  LOAD_FN_PTR(id3tag_set_fieldvalue)
-#endif
-  LOAD_FN_PTR(lame_init_params)
-  LOAD_FN_PTR(lame_set_errorf)
-  LOAD_FN_PTR(lame_set_debugf)
-  LOAD_FN_PTR(lame_set_msgf)
-  LOAD_FN_PTR(lame_encode_buffer)
-  LOAD_FN_PTR(lame_encode_flush)
-  LOAD_FN_PTR(lame_close)
-  LOAD_FN_PTR(lame_mp3_tags_fid)
-  LOAD_FN_PTR(lame_get_brate)
-  LOAD_FN_PTR(lame_set_brate)
-  LOAD_FN_PTR(lame_set_quality)
-  LOAD_FN_PTR(lame_set_VBR)
-  LOAD_FN_PTR(lame_set_VBR_min_bitrate_kbps)
-#if HAVE_LAME_SET_VBR_QUALITY
-  LOAD_FN_PTR(lame_set_VBR_quality)
-#endif
-  LOAD_FN_PTR(lame_get_VBR)
+  /* read 10 bytes in case there's an ID3 version 2 header here */
+  bytes_read = fread(id3v2_header, 1, sizeof(id3v2_header), fp);
+  if (bytes_read != sizeof(id3v2_header)) {
+    lsx_warn("cannot update id3 tag - failed to read id3 header");
+    return SOX_EOF;      /* not readable, maybe opened Write-Only */
+  }
 
-#undef LOAD_FN_PTR
-#undef DL_LIB_NAME
+  /* does the stream begin with the ID3 version 2 file identifier? */
+  if (!strncmp((char *) id3v2_header, "ID3", 3)) {
+    /* the tag size (minus the 10-byte header) is encoded into four
+     * bytes where the most significant bit is clear in each byte */
+    id3v2_size = (((id3v2_header[6] & 0x7f) << 21)
+                    | ((id3v2_header[7] & 0x7f) << 14)
+                    | ((id3v2_header[8] & 0x7f) << 7)
+                    | (id3v2_header[9] & 0x7f))
+        + sizeof(id3v2_header);
+  } else {
+    /* no ID3 version 2 tag in this stream */
+    id3v2_size = 0;
+  }
+  return id3v2_size;
+}
 
+static void rewrite_id3v2_tag(sox_format_t * ft, int id3v2_size, unsigned long num_samples)
+{
+  priv_t *p = (priv_t *)ft->priv;
+  FILE *fp = ft->fp;
+  int new_size;
+  unsigned char *buffer = lsx_malloc(id3v2_size);  
+
+  if (!buffer)
+  {
+    lsx_warn("cannot update id3 tag - failed to allocate buffer");
+    return;
+  }
+
+  p->lame_set_num_samples(p->gfp, num_samples);
+  lsx_debug("updated MP3 TLEN to %lu samples", num_samples);
+
+  p->id3tag_set_pad(p->gfp, ID3PADDING);
+  new_size = p->lame_get_id3v2_tag(p->gfp, buffer, id3v2_size);
+
+  if (new_size != id3v2_size && new_size-ID3PADDING <= id3v2_size) {
+    p->id3tag_set_pad(p->gfp, ID3PADDING + id3v2_size - new_size);
+    new_size = p->lame_get_id3v2_tag(p->gfp, buffer, id3v2_size);
+  }
+
+  if (new_size != id3v2_size) {
+    lsx_warn("cannot update id3 tag - new tag too big");
+  } else {
+    fseeko(fp, 0, SEEK_SET);
+    /* Overwrite the Id3v2 tag (this time TLEN should be accurate) */
+    if (fwrite(buffer, id3v2_size, 1, fp) != 1) {
+      lsx_debug("Rewrote Id3v2 tag (%d bytes)", id3v2_size);
+    }
+  }
+
+  free(buffer);
+}
+
+static void rewrite_tags(sox_format_t * ft, unsigned long num_samples)
+{
+  priv_t *p = (priv_t *)ft->priv;
+  FILE *fp = ft->fp;
+
+  off_t file_size;
+  int id3v2_size;
+
+  if (fseeko(fp, 0, SEEK_END)) {
+    lsx_warn("cannot update tags - seek to end failed");
+    return;
+  }
+
+  /* Get file size */
+  file_size = ftello(fp);
+
+  if (file_size == 0) {
+    lsx_warn("cannot update tags - file size is 0");
+    return;
+  }
+
+  id3v2_size = get_id3v2_tag_size(ft);
+  if (id3v2_size < 0) {
+    return;
+  } else if (id3v2_size > 0 && num_samples != p->num_samples) {
+    rewrite_id3v2_tag(ft, id3v2_size, num_samples);
+  }
+
+  if (p->vbr_tag) {
+    unsigned int lametag_size;
+    uint8_t buffer[MAXFRAMESIZE];
+
+    if (fseeko(fp, id3v2_size, SEEK_SET)) {
+      lsx_warn("cannot write VBR tag - seek to tag block failed");
+      return;
+    }
+
+    lametag_size = p->lame_get_lametag_frame(p->gfp, buffer, sizeof(buffer));
+    if (lametag_size > sizeof(buffer)) {
+      lsx_warn("cannot write VBR tag - VBR tag too large for buffer");
+      return;
+    }
+
+    if (lametag_size < 1) {
+      return;
+    }
+
+    if (fwrite(buffer, lametag_size, 1, fp) != 1) {
+      lsx_warn("cannot write VBR tag - VBR tag write failed");
+    } else {
+      lsx_debug("rewrote VBR tag (%u bytes)", lametag_size);
+    }
+  }
+}
+
+#define LAME_BUFFER_SIZE(num_samples) (((num_samples) + 3) / 4 * 5 + 7200)
+
+static int startwrite(sox_format_t * ft)
+{
+  priv_t *p = (priv_t *) ft->priv;
+  int openlibrary_result;
+
+  LSX_DLLIBRARY_OPEN(
+      p,
+      lame_dl,
+      LAME_FUNC_ENTRIES,
+      "LAME encoder library",
+      lame_library_names,
+      openlibrary_result);
+  if (openlibrary_result)
+    return SOX_EOF;
+
   if (ft->encoding.encoding != SOX_ENCODING_MP3) {
     if(ft->encoding.encoding != SOX_ENCODING_UNKNOWN)
       lsx_report("Encoding forced to MP3");
@@ -560,6 +694,12 @@
     ft->encoding.encoding = SOX_ENCODING_MP3;
   }
 
+  p->mp3_buffer_size = LAME_BUFFER_SIZE(sox_globals.bufsiz);
+  p->mp3_buffer = lsx_malloc(p->mp3_buffer_size);
+
+  p->pcm_buffer_size = sox_globals.bufsiz * sizeof(short signed int);
+  p->pcm_buffer = lsx_malloc(p->pcm_buffer_size);
+
   p->gfp = p->lame_init();
 
   if (p->gfp == NULL){
@@ -572,6 +712,9 @@
   p->lame_set_debugf(p->gfp,debugf);
   p->lame_set_msgf  (p->gfp,msgf);
 
+  p->num_samples = ft->signal.length == SOX_IGNORE_LENGTH ? 0 : ft->signal.length / max(ft->signal.channels, 1);
+  p->lame_set_num_samples(p->gfp, p->num_samples);
+
   if (ft->signal.channels != SOX_ENCODING_UNKNOWN) {
     if ( (p->lame_set_num_channels(p->gfp,(int)ft->signal.channels)) < 0) {
         lsx_fail_errno(ft,SOX_EOF,"Unsupported number of channels");
@@ -582,13 +725,11 @@
     ft->signal.channels = p->lame_get_num_channels(p->gfp); /* LAME default */
 
   p->lame_set_in_samplerate(p->gfp,(int)ft->signal.rate);
+  p->lame_set_out_samplerate(p->gfp,(int)ft->signal.rate);
 
-  p->lame_set_bWriteVbrTag(p->gfp, 0); /* disable writing VBR tag */
+  if (!LSX_DLFUNC_IS_STUB(p, id3tag_init))
+    write_comments(ft);
 
-#ifdef HAVE_ID3TAG_SET_FIELDVALUE
-  write_comments(ft);
-#endif
-
   /* The primary parameter to the LAME encoder is the bit rate. If the
    * value of encoding.compression is a positive integer, it's taken as
    * the bitrate in kbps (that is if you specify 128, it use 128 kbps).
@@ -602,9 +743,8 @@
    * Because encoding.compression is a float, the fractional part is used
    * to select quality. 128.2 selects 128 kbps encoding with a quality
    * of 2. There is one problem with this approach. We need 128 to specify
-   * 128 kbps encoding with default quality, so 0 means use default. Instead
-   * of 0 you have to use .01 (or .99) to specify the highest quality
-   * (128.01 or 128.99).
+   * 128 kbps encoding with default quality, so .0 means use default. Instead
+   * of .0 you have to use .01 to specify the highest quality (128.01).
    *
    * LAME uses bitrate to specify a constant bitrate, but higher quality
    * can be achieved using Variable Bit Rate (VBR). VBR quality (really
@@ -634,64 +774,61 @@
     double abs_compression = fabs(ft->encoding.compression);
     double floor_compression = floor(abs_compression);
     double fraction_compression = abs_compression - floor_compression;
+    int bitrate_q = (int)floor_compression;
+    int encoder_q =
+        fraction_compression == 0.0
+        ? -1
+        : (int)(fraction_compression * 10.0 + 0.5);
 
-    if (floor(ft->encoding.compression) <= 0) {
+    if (ft->encoding.compression < 0.5) {
       if (p->lame_get_VBR(p->gfp) == vbr_off)
         p->lame_set_VBR(p->gfp, vbr_default);
 
       if (ft->seekable) {
-        p->lame_set_bWriteVbrTag(p->gfp, 1); /* enable writing VBR tag */
+        if (!LSX_DLFUNC_IS_STUB(p, lame_get_id3v2_tag) &&
+            !LSX_DLFUNC_IS_STUB(p, lame_get_lametag_frame)) {
+          p->vbr_tag = 1;
+        } else {
+          lsx_warn("unable to write VBR tag because lametag support is not present");
+        }
       } else {
-        lsx_warn("unable to write VBR Tag because we can't seek");
+        lsx_warn("unable to write VBR tag because we can't seek");
       }
 
-#if HAVE_LAME_SET_VBR_QUALITY
-#define IGNORE_WARNING \
-      if (p->lame_set_VBR_quality(p->gfp, floor_compression) < 0)
-#include "ignore-warning-1.h"
+      if (p->lame_set_VBR_q(p->gfp, bitrate_q) < 0)
       {
         lsx_fail_errno(ft, SOX_EOF,
-          "lame_set_VBR_quality(%f) failed (should be between 0 and 9)",
-          floor_compression);
+          "lame_set_VBR_q(%d) failed (should be between 0 and 9)",
+          bitrate_q);
         return(SOX_EOF);
       }
-      lsx_report("lame_set_VBR_quality(%f)", floor_compression);
-#else
-      /* TODO lsx_warn */
-#endif
+      lsx_report("lame_set_VBR_q(%d)", bitrate_q);
     } else {
-      if (p->lame_set_brate(p->gfp, (int)floor_compression) < 0) {
+      if (p->lame_set_brate(p->gfp, bitrate_q) < 0) {
         lsx_fail_errno(ft, SOX_EOF,
-          "lame_set_brate(%d) failed", (int)floor_compression);
+          "lame_set_brate(%d) failed", bitrate_q);
         return(SOX_EOF);
       }
-      p->lame_set_VBR_min_bitrate_kbps(p->gfp, p->lame_get_brate(p->gfp));
-      lsx_report("lame_set_brate(%d)", (int)floor_compression);
+      lsx_report("lame_set_brate(%d)", bitrate_q);
     }
 
     /* Set Quality */
 
-    if (0.0 == fraction_compression) {
+    if (encoder_q < 0) {
       /* use default quality value */
       lsx_report("using MP3 default quality");
-    }
-    else if (fraction_compression <= 0.01 || 0.99 <= fraction_compression) {
-      if (p->lame_set_quality(p->gfp, 0) < 0) {
-        lsx_fail_errno(ft, SOX_EOF, "lame_set_quality(0) failed");
-        return(SOX_EOF);
-      }
-      lsx_report("lame_set_quality(0)");
     } else {
-      int quality = (int)(0.5 + fraction_compression * 10);
-      if (p->lame_set_quality(p->gfp, quality) < 0) {
+      if (p->lame_set_quality(p->gfp, encoder_q) < 0) {
         lsx_fail_errno(ft, SOX_EOF,
-          "lame_set_quality(%d) failed", quality);
+          "lame_set_quality(%d) failed", encoder_q);
         return(SOX_EOF);
       }
-      lsx_report("lame_set_quality(%d)", quality);
+      lsx_report("lame_set_quality(%d)", encoder_q);
     }
   }
 
+  p->lame_set_bWriteVbrTag(p->gfp, p->vbr_tag);
+
   if (p->lame_init_params(p->gfp) < 0){
         lsx_fail_errno(ft,SOX_EOF,"LAME initialization failed");
         return(SOX_EOF);
@@ -703,15 +840,24 @@
 static size_t sox_mp3write(sox_format_t * ft, const sox_sample_t *buf, size_t samp)
 {
     priv_t *p = (priv_t *)ft->priv;
-    unsigned char *mp3buffer;
-    size_t mp3buffer_size;
+    size_t new_buffer_size;
     short signed int *buffer_l, *buffer_r = NULL;
     int nsamples = samp/ft->signal.channels;
     int i,j;
-    ptrdiff_t done = 0;
     size_t written;
     SOX_SAMPLE_LOCALS;
 
+    new_buffer_size = samp * sizeof(short signed int);
+    if (p->pcm_buffer_size < new_buffer_size) {
+      short *new_buffer = lsx_realloc(p->pcm_buffer, new_buffer_size);
+      if (!new_buffer) {
+        lsx_fail_errno(ft, SOX_ENOMEM, "Out of memory");
+        return 0;
+      }
+      p->pcm_buffer_size = new_buffer_size;
+      p->pcm_buffer = new_buffer;
+    }
+
     /* NOTE: This logic assumes that "short int" is 16-bits
      * on all platforms.  It happens to be for all that I know
      * about.
@@ -728,7 +874,7 @@
      * lsx_malloc()'ing a smaller buffer and call a consistent
      * interface.
      */
-    buffer_l = lsx_malloc(nsamples * sizeof(short signed int));
+    buffer_l = p->pcm_buffer;
 
     if (ft->signal.channels == 2)
     {
@@ -735,7 +881,7 @@
         /* lame doesn't support iterleaved samples so we must break
          * them out into seperate buffers.
          */
-        buffer_r = lsx_malloc(nsamples* sizeof(short signed int));
+        buffer_r = p->pcm_buffer + nsamples;
         j=0;
         for (i=0; i<nsamples; i++)
         {
@@ -750,59 +896,55 @@
             buffer_l[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++], ft->clips);
     }
 
-    mp3buffer_size = 1.25 * nsamples + 7200;
-    mp3buffer = lsx_malloc(mp3buffer_size);
+    new_buffer_size = LAME_BUFFER_SIZE(samp);
+    if (p->mp3_buffer_size < new_buffer_size) {
+      unsigned char *new_buffer = lsx_realloc(p->mp3_buffer, new_buffer_size);
+      if (!new_buffer) {
+        lsx_fail_errno(ft, SOX_ENOMEM, "Out of memory");
+        return 0;
+      }
+      p->mp3_buffer_size = new_buffer_size;
+      p->mp3_buffer = new_buffer;
+    }
 
     if ((written =
-	 p->lame_encode_buffer(p->gfp,buffer_l, buffer_r,
-			       nsamples, mp3buffer,
-			       (int)mp3buffer_size)) > mp3buffer_size){
+      p->lame_encode_buffer(p->gfp,buffer_l, buffer_r,
+                   nsamples, p->mp3_buffer,
+                   (int)p->mp3_buffer_size)) > p->mp3_buffer_size){
         lsx_fail_errno(ft,SOX_EOF,"Encoding failed");
-        goto end;
+        return 0;
     }
 
-    if (lsx_writebuf(ft, mp3buffer, written) < written)
+    if (lsx_writebuf(ft, p->mp3_buffer, written) < written)
     {
         lsx_fail_errno(ft,SOX_EOF,"File write failed");
-        goto end;
+        return 0;
     }
 
-    done = nsamples*ft->signal.channels;
-
-end:
-    free(mp3buffer);
-    if (ft->signal.channels == 2)
-        free(buffer_r);
-    free(buffer_l);
-
-    return done;
+    return samp;
 }
 
 static int stopwrite(sox_format_t * ft)
 {
   priv_t *p = (priv_t *) ft->priv;
-  unsigned char mp3buffer[7200];
-  int written = p->lame_encode_flush(p->gfp, mp3buffer, (int)sizeof(mp3buffer));
+  unsigned long num_samples = ft->olength == SOX_IGNORE_LENGTH ? 0 : ft->olength / max(ft->signal.channels, 1);
+  int written = p->lame_encode_flush(p->gfp, p->mp3_buffer, (int)p->mp3_buffer_size);
 
   if (written < 0)
     lsx_fail_errno(ft, SOX_EOF, "Encoding failed");
-  else if (lsx_writebuf(ft, mp3buffer, (size_t)written) < (size_t)written)
-  {
+  else if (lsx_writebuf(ft, p->mp3_buffer, (size_t)written) < (size_t)written)
     lsx_fail_errno(ft, SOX_EOF, "File write failed");
-  }
-  else if (p->lame_get_bWriteVbrTag(p->gfp) && ft->seekable) {
-    p->lame_mp3_tags_fid(p->gfp, ft->fp);
-  }
+  else if (ft->seekable && (num_samples != p->num_samples || p->vbr_tag))
+    rewrite_tags(ft, num_samples);
 
+  free(p->mp3_buffer);
+  free(p->pcm_buffer);
   p->lame_close(p->gfp);
-#if defined HAVE_LIBLTDL && defined DL_LAME
-  if (!lt_dlclose(p->lame_lth))
-    lt_dlexit();
-#endif
+  LSX_DLLIBRARY_CLOSE(p, lame_dl);
   return SOX_SUCCESS;
 }
 
-#else /* HAVE_LAME_LAME_H */
+#else /* HAVE_LAME */
 static int startwrite(sox_format_t * ft UNUSED)
 {
   lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP3 encoding support");
@@ -810,7 +952,7 @@
 }
 #define sox_mp3write NULL
 #define stopwrite NULL
-#endif /* HAVE_LAME_LAME_H */
+#endif /* HAVE_LAME */
 
 LSX_FORMAT_HANDLER(mp3)
 {
@@ -825,4 +967,4 @@
   };
   return &handler;
 }
-#endif /* defined(HAVE_LAME_LAME_H) || defined (HAVE_MAD_H) */
+#endif /* defined(HAVE_MAD_H) || defined(HAVE_LAME) */
--- a/src/sndfile.c
+++ b/src/sndfile.c
@@ -30,10 +30,49 @@
 
 #define LOG_MAX 2048 /* As per the SFC_GET_LOG_INFO example */
 
-#ifndef HACKED_LSF
-#define sf_stop(x)
+#if !defined(HAVE_LIBLTDL) || !defined(HAVE_SNDFILE_1_0_12)
+#undef DL_SNDFILE
 #endif
 
+static const char* const sndfile_library_names[] =
+{
+#ifdef DL_SNDFILE
+  "libsndfile-1",
+  "cygsndfile-1",
+#endif
+  NULL
+};
+
+#ifdef DL_SNDFILE
+  #define SNDFILE_FUNC      LSX_DLENTRY_DYNAMIC
+  #define SNDFILE_FUNC_STOP LSX_DLENTRY_STUB
+#else
+  #define SNDFILE_FUNC      LSX_DLENTRY_STATIC
+#ifdef HACKED_LSF
+  #define SNDFILE_FUNC_STOP LSX_DLENTRY_STATIC
+#else
+  #define SNDFILE_FUNC_STOP LSX_DLENTRY_STUB
+#endif
+#endif /* DL_SNDFILE */
+
+#ifdef HAVE_SNDFILE_1_0_12
+#define SNDFILE_FUNC_OPEN(f,x) \
+  SNDFILE_FUNC(f,x, SNDFILE*, sf_open_virtual, (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data))
+#else
+  SNDFILE_FUNC(f,x, SNDFILE*, sf_open_fd, (int fd, int mode, SF_INFO *sfinfo, int close_desc))
+#endif
+
+#define SNDFILE_FUNC_ENTRIES(f,x) \
+  SNDFILE_FUNC_OPEN(f,x) \
+  SNDFILE_FUNC_STOP(f,x, int, sf_stop, (SNDFILE *sndfile)) \
+  SNDFILE_FUNC(f,x, int, sf_close, (SNDFILE *sndfile)) \
+  SNDFILE_FUNC(f,x, int, sf_format_check, (const SF_INFO *info)) \
+  SNDFILE_FUNC(f,x, int, sf_command, (SNDFILE *sndfile, int command, void *data, int datasize)) \
+  SNDFILE_FUNC(f,x, sf_count_t, sf_read_int, (SNDFILE *sndfile, int *ptr, sf_count_t items)) \
+  SNDFILE_FUNC(f,x, sf_count_t, sf_write_int, (SNDFILE *sndfile, const int *ptr, sf_count_t items)) \
+  SNDFILE_FUNC(f,x, sf_count_t, sf_seek, (SNDFILE *sndfile, sf_count_t frames, int whence)) \
+  SNDFILE_FUNC(f,x, const char*, sf_strerror, (SNDFILE *sndfile))
+
 /* Private data for sndfile files */
 typedef struct {
   SNDFILE *sf_file;
@@ -40,6 +79,7 @@
   SF_INFO *sf_info;
   char * log_buffer;
   char const * log_buffer_ptr;
+  LSX_DLENTRIES_TO_PTRS(SNDFILE_FUNC_ENTRIES, sndfile_dl);
 } priv_t;
 
 /*
@@ -48,7 +88,7 @@
 static void drain_log_buffer(sox_format_t * ft)
 {
   priv_t * sf = (priv_t *)ft->priv;
-  sf_command(sf->sf_file, SFC_GET_LOG_INFO, sf->log_buffer, LOG_MAX);
+  sf->sf_command(sf->sf_file, SFC_GET_LOG_INFO, sf->log_buffer, LOG_MAX);
   while (*sf->log_buffer_ptr) {
     static char const warning_prefix[] = "*** Warning : ";
     char const * end = strchr(sf->log_buffer_ptr, '\n');
@@ -181,6 +221,49 @@
   { "xi",       SF_FORMAT_XI }
 };
 
+static int sf_stop_stub(SNDFILE *sndfile)
+{
+    return 1;
+}
+
+#ifdef HAVE_SNDFILE_1_0_12
+
+static sf_count_t vio_get_filelen(void *user_data)
+{
+  return lsx_filelength((sox_format_t *)user_data);
+}
+
+static sf_count_t vio_seek(sf_count_t offset, int whence, void *user_data)
+{
+    return lsx_seeki((sox_format_t *)user_data, (off_t)offset, whence);
+}
+
+static sf_count_t vio_read(void *ptr, sf_count_t count, void *user_data)
+{
+    return lsx_readbuf((sox_format_t *)user_data, ptr, (size_t)count);
+}
+
+static sf_count_t vio_write(const void *ptr, sf_count_t count, void *user_data)
+{
+    return lsx_writebuf((sox_format_t *)user_data, ptr, (size_t)count);
+}
+
+static sf_count_t vio_tell(void *user_data)
+{
+    return lsx_tell((sox_format_t *)user_data);
+}
+
+static SF_VIRTUAL_IO vio =
+{
+    vio_get_filelen,
+    vio_seek,
+    vio_read,
+    vio_write,
+    vio_tell
+};
+
+#endif /* HAVE_SNDFILE_1_0_12 */
+
 /* Convert file name or type to libsndfile format */
 static int name_to_format(const char *name)
 {
@@ -205,10 +288,22 @@
   return 0;
 }
 
-static void start(sox_format_t * ft)
+static int start(sox_format_t * ft)
 {
   priv_t * sf = (priv_t *)ft->priv;
   int subtype = ft_enc(ft->encoding.bits_per_sample? ft->encoding.bits_per_sample : ft->signal.precision, ft->encoding.encoding);
+  int open_library_result;
+
+  LSX_DLLIBRARY_OPEN(
+      sf,
+      sndfile_dl,
+      SNDFILE_FUNC_ENTRIES,
+      "libsndfile library",
+      sndfile_library_names,
+      open_library_result);
+  if (open_library_result)
+    return SOX_EOF;
+
   sf->log_buffer_ptr = sf->log_buffer = lsx_malloc((size_t)LOG_MAX);
   sf->sf_info = lsx_calloc(1, sizeof(SF_INFO));
 
@@ -219,16 +314,18 @@
     else
       sf->sf_info->format = name_to_format(ft->filetype) | subtype;
   }
-  sf->sf_info->samplerate = ft->signal.rate;
+  sf->sf_info->samplerate = (int)ft->signal.rate;
   sf->sf_info->channels = ft->signal.channels;
   if (ft->signal.channels)
     sf->sf_info->frames = ft->signal.length / ft->signal.channels;
+
+  return SOX_SUCCESS;
 }
 
 static int check_read_params(sox_format_t * ft, unsigned channels,
     sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, off_t length)
 {
-  ft->signal.length = length;
+  ft->signal.length = (size_t)length;
 
   if (channels && ft->signal.channels && ft->signal.channels != channels)
     lsx_warn("`%s': overriding number of channels", ft->filename);
@@ -262,15 +359,20 @@
   sox_encoding_t encoding;
   sox_rate_t rate;
 
-  start(ft);
+  if (start(ft) == SOX_EOF)
+      return SOX_EOF;
 
-  sf->sf_file = sf_open_fd(fileno(ft->fp), SFM_READ, sf->sf_info, 1);
+#ifdef HAVE_SNDFILE_1_0_12
+  sf->sf_file = sf->sf_open_virtual(&vio, SFM_READ, sf->sf_info, ft);
+#else
+  sf->sf_file = sf->sf_open_fd(fileno(ft->fp), SFM_READ, sf->sf_info, 1);
   ft->fp = NULL; /* Transfer ownership of fp to LSF */
+#endif
   drain_log_buffer(ft);
 
   if (sf->sf_file == NULL) {
     memset(ft->sox_errstr, 0, sizeof(ft->sox_errstr));
-    strncpy(ft->sox_errstr, sf_strerror(sf->sf_file), sizeof(ft->sox_errstr)-1);
+    strncpy(ft->sox_errstr, sf->sf_strerror(sf->sf_file), sizeof(ft->sox_errstr)-1);
     free(sf->sf_file);
     return SOX_EOF;
   }
@@ -289,8 +391,8 @@
 
 #ifdef HAVE_SFC_SET_SCALE_FLOAT_INT_READ
   if ((sf->sf_info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
-    sf_command(sf->sf_file, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
-    sf_command(sf->sf_file, SFC_SET_CLIPPING, NULL, SF_TRUE);
+    sf->sf_command(sf->sf_file, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
+    sf->sf_command(sf->sf_file, SFC_SET_CLIPPING, NULL, SF_TRUE);
   }
 #endif
 
@@ -310,7 +412,7 @@
 {
   priv_t * sf = (priv_t *)ft->priv;
   /* FIXME: We assume int == sox_sample_t here */
-  return (size_t)sf_read_int(sf->sf_file, (int *)buf, (sf_count_t)len);
+  return (size_t)sf->sf_read_int(sf->sf_file, (int *)buf, (sf_count_t)len);
 }
 
 /*
@@ -319,9 +421,10 @@
 static int stopread(sox_format_t * ft)
 {
   priv_t * sf = (priv_t *)ft->priv;
-  sf_stop(sf->sf_file);
+  sf->sf_stop(sf->sf_file);
   drain_log_buffer(ft);
-  sf_close(sf->sf_file);
+  sf->sf_close(sf->sf_file);
+  LSX_DLLIBRARY_CLOSE(sf, sndfile_dl);
   return SOX_SUCCESS;
 }
 
@@ -328,16 +431,19 @@
 static int startwrite(sox_format_t * ft)
 {
   priv_t * sf = (priv_t *)ft->priv;
-  start(ft);
+
+  if (start(ft) == SOX_EOF)
+      return SOX_EOF;
+
   /* If output format is invalid, try to find a sensible default */
-  if (!sf_format_check(sf->sf_info)) {
+  if (!sf->sf_format_check(sf->sf_info)) {
     SF_FORMAT_INFO format_info;
     int i, count;
 
-    sf_command(sf->sf_file, SFC_GET_SIMPLE_FORMAT_COUNT, &count, (int) sizeof(int));
+    sf->sf_command(sf->sf_file, SFC_GET_SIMPLE_FORMAT_COUNT, &count, (int) sizeof(int));
     for (i = 0; i < count; i++) {
       format_info.format = i;
-      sf_command(sf->sf_file, SFC_GET_SIMPLE_FORMAT, &format_info, (int) sizeof(format_info));
+      sf->sf_command(sf->sf_file, SFC_GET_SIMPLE_FORMAT, &format_info, (int) sizeof(format_info));
       if ((format_info.format & SF_FORMAT_TYPEMASK) == (sf->sf_info->format & SF_FORMAT_TYPEMASK)) {
         sf->sf_info->format = format_info.format;
         /* FIXME: Print out exactly what we chose, needs sndfile ->
@@ -346,7 +452,7 @@
       }
     }
 
-    if (!sf_format_check(sf->sf_info)) {
+    if (!sf->sf_format_check(sf->sf_info)) {
       lsx_fail("cannot find a usable output encoding");
       return SOX_EOF;
     }
@@ -354,13 +460,17 @@
       lsx_warn("cannot use desired output encoding, choosing default");
   }
 
-  sf->sf_file = sf_open_fd(fileno(ft->fp), SFM_WRITE, sf->sf_info, 1);
+#ifdef HAVE_SNDFILE_1_0_12
+  sf->sf_file = sf->sf_open_virtual(&vio, SFM_WRITE, sf->sf_info, ft);
+#else
+  sf->sf_file = sf->sf_open_fd(fileno(ft->fp), SFM_WRITE, sf->sf_info, 1);
   ft->fp = NULL; /* Transfer ownership of fp to LSF */
+#endif
   drain_log_buffer(ft);
 
   if (sf->sf_file == NULL) {
     memset(ft->sox_errstr, 0, sizeof(ft->sox_errstr));
-    strncpy(ft->sox_errstr, sf_strerror(sf->sf_file), sizeof(ft->sox_errstr)-1);
+    strncpy(ft->sox_errstr, sf->sf_strerror(sf->sf_file), sizeof(ft->sox_errstr)-1);
     free(sf->sf_file);
     return SOX_EOF;
   }
@@ -367,7 +477,7 @@
 
 #ifdef HAVE_SFC_SET_SCALE_INT_FLOAT_WRITE
   if ((sf->sf_info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT)
-    sf_command(sf->sf_file, SFC_SET_SCALE_INT_FLOAT_WRITE, NULL, SF_TRUE);
+    sf->sf_command(sf->sf_file, SFC_SET_SCALE_INT_FLOAT_WRITE, NULL, SF_TRUE);
 #endif
            
   return SOX_SUCCESS;
@@ -381,7 +491,7 @@
 {
   priv_t * sf = (priv_t *)ft->priv;
   /* FIXME: We assume int == sox_sample_t here */
-  return (size_t)sf_write_int(sf->sf_file, (int *)buf, (sf_count_t)len);
+  return (size_t)sf->sf_write_int(sf->sf_file, (int *)buf, (sf_count_t)len);
 }
 
 /*
@@ -390,9 +500,10 @@
 static int stopwrite(sox_format_t * ft)
 {
   priv_t * sf = (priv_t *)ft->priv;
-  sf_stop(sf->sf_file);
+  sf->sf_stop(sf->sf_file);
   drain_log_buffer(ft);
-  sf_close(sf->sf_file);
+  sf->sf_close(sf->sf_file);
+  LSX_DLLIBRARY_CLOSE(sf, sndfile_dl);
   return SOX_SUCCESS;
 }
 
@@ -399,7 +510,7 @@
 static int seek(sox_format_t * ft, uint64_t offset)
 {
   priv_t * sf = (priv_t *)ft->priv;
-  sf_seek(sf->sf_file, (sf_count_t)(offset / ft->signal.channels), SEEK_CUR);
+  sf->sf_seek(sf->sf_file, (sf_count_t)(offset / ft->signal.channels), SEEK_CUR);
   return SOX_SUCCESS;
 }
 
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -133,11 +133,6 @@
 #define LSX_MAX_TBW3A floor(LSX_MAX_TBW0A * LSX_TO_3dB)
 void lsx_plot_fir(double * h, int num_points, sox_rate_t rate, sox_plot_t type, char const * title, double y1, double y2);
 
-#ifndef HAVE_STRCASECMP
-int strcasecmp(const char *s1, const char *s2);
-int strncasecmp(char const * s1, char const * s2, size_t n);
-#endif
-
 #ifdef HAVE_BYTESWAP_H
 #include <byteswap.h>
 #define lsx_swapw(x) bswap_16(x)
@@ -309,5 +304,98 @@
 
 int lsx_effects_init(void);
 int lsx_effects_quit(void);
+
+/*--------------------------------- Dynamic Library ----------------------------------*/
+
+#if defined(HAVE_LIBLTDL)
+    #include <ltdl.h>
+    typedef lt_dlhandle lsx_dlhandle;
+#else
+    struct lsx_dlhandle_tag;
+    typedef struct lsx_dlhandle_tag *lsx_dlhandle;
+#endif
+
+typedef void (*lsx_dlptr)(void);
+
+typedef struct lsx_dlfunction_info
+{
+    const char* name;
+    lsx_dlptr static_func;
+    lsx_dlptr stub_func;
+} lsx_dlfunction_info;
+
+int lsx_open_dllibrary(
+    const char* library_description,
+    const char * const library_names[],
+    const lsx_dlfunction_info func_infos[],
+    lsx_dlptr selected_funcs[],
+    lsx_dlhandle* pdl);
+
+void lsx_close_dllibrary(
+    lsx_dlhandle dl);
+
+#define LSX_DLENTRIES_APPLY__(entries, f, x) entries(f, x)
+
+#define LSX_DLENTRY_TO_PTR__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \
+    func_return (*func_ptr) func_args;
+
+/* LSX_DLENTRIES_TO_PTRS: Given an ENTRIES macro and the name of the dlhandle
+   variable, declares the corresponding function pointer variables and the
+   dlhandle variable. */
+#define LSX_DLENTRIES_TO_PTRS(entries, dlhandle) \
+    LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRY_TO_PTR__, 0) \
+    lsx_dlhandle dlhandle
+
+#define LSX_DLLIBRARY_OPEN1__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \
+    { #func_name, (lsx_dlptr)(static_func), (lsx_dlptr)(stub_func) },
+
+#define LSX_DLLIBRARY_OPEN2__(ptr_container, func_return, func_name, func_args, static_func, stub_func, func_ptr) \
+    (ptr_container)->func_ptr = (func_return (*)func_args)lsx_dlfunction_open_library_funcs[lsx_dlfunction_open_library_index++];
+
+/* LSX_DLFUNCTION_OPEN_LIBRARY: Input an ENTRIES macro, the library's description,
+   a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }),
+   the name of the dlhandle variable, the name of the structure that contains
+   the function pointer and dlhandle variables, and the name of the variable in
+   which the result of the lsx_open_dllibrary call should be stored. This will
+   call lsx_open_dllibrary and copy the resulting function pointers into the
+   structure members. */
+#define LSX_DLLIBRARY_OPEN(ptr_container, dlhandle, entries, library_description, library_names, return_var) \
+    do { \
+      lsx_dlfunction_info lsx_dlfunction_open_library_infos[] = { \
+        LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN1__, 0) \
+        {NULL,NULL,NULL} }; \
+      int lsx_dlfunction_open_library_index = 0; \
+      lsx_dlptr lsx_dlfunction_open_library_funcs[sizeof(lsx_dlfunction_open_library_infos)/sizeof(lsx_dlfunction_open_library_infos[0])]; \
+      (return_var) = lsx_open_dllibrary((library_description), (library_names), lsx_dlfunction_open_library_infos, lsx_dlfunction_open_library_funcs, &(ptr_container)->dlhandle); \
+      LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN2__, ptr_container) \
+    } while(0)
+
+#define LSX_DLLIBRARY_CLOSE(ptr_container, dlhandle) \
+    lsx_close_dllibrary((ptr_container)->dlhandle)
+
+  /* LSX_DLENTRY_STATIC: For use in creating an ENTRIES macro. func is
+     expected to be available at link time. If not present, link will fail. */
+#define LSX_DLENTRY_STATIC(f,x, ret, func, args)  f(x, ret, func, args, func, NULL, func)
+
+  /* LSX_DLENTRY_DYNAMIC: For use in creating an ENTRIES macro. func need
+     not be available at link time (and if present, the link time version will
+     not be used). func will be loaded via dlsym. If this function is not
+     found in the shared library, the shared library will not be used. */
+#define LSX_DLENTRY_DYNAMIC(f,x, ret, func, args) f(x, ret, func, args, NULL, NULL, func)
+
+  /* LSX_DLENTRY_STUB: For use in creating an ENTRIES macro. func need not
+     be available at link time (and if present, the link time version will not
+     be used). If using DL_LAME, the func may be loaded via dlopen/dlsym, but
+     if not found, the shared library will still be used if all of the
+     non-stub functions are found. If the function is not found via dlsym (or
+     if we are not loading any shared libraries), the stub will be used. This
+     assumes that the name of the stub function is the name of the function +
+     "_stub". */
+#define LSX_DLENTRY_STUB(f,x, ret, func, args)    f(x, ret, func, args, NULL, func##_stub, func)
+
+  /* LSX_DLFUNC_IS_STUB: returns true if the named function is a do-nothing
+     stub. Assumes that the name of the stub function is the name of the
+     function + "_stub". */
+#define LSX_DLFUNC_IS_STUB(ptr_container, func) ((ptr_container)->func == func##_stub)
 
 #endif
--- a/src/util.c
+++ b/src/util.c
@@ -21,21 +21,31 @@
 #include <ctype.h>
 #include <stdio.h>
 
-#ifndef HAVE_STRCASECMP
-int strcasecmp(const char * s1, const char * s2)
+int lsx_strcasecmp(const char * s1, const char * s2)
 {
+#if defined(HAVE_STRCASECMP)
+  return strcasecmp(s1, s2);
+#elif defined(_MSC_VER)
+  return _stricmp(s1, s2);
+#else
   while (*s1 && (toupper(*s1) == toupper(*s2)))
     s1++, s2++;
   return toupper(*s1) - toupper(*s2);
+#endif
 }
 
-int strncasecmp(char const * s1, char const * s2, size_t n)
+int lsx_strncasecmp(char const * s1, char const * s2, size_t n)
 {
+#if defined(HAVE_STRCASECMP)
+  return strncasecmp(s1, s2, n);
+#elif defined(_MSC_VER)
+  return _strnicmp(s1, s2, n);
+#else
   while (--n && *s1 && (toupper(*s1) == toupper(*s2)))
     s1++, s2++;
   return toupper(*s1) - toupper(*s2);
-}
 #endif
+}
 
 sox_bool lsx_strends(char const * str, char const * end)
 {
@@ -136,3 +146,138 @@
   return string[n];
 }
 
+int lsx_open_dllibrary(
+  const char* library_description,
+  const char* const library_names[] UNUSED,
+  const lsx_dlfunction_info func_infos[],
+  lsx_dlptr selected_funcs[],
+  lsx_dlhandle* pdl)
+{
+  int failed = 0;
+  lsx_dlhandle dl = NULL;
+
+  /* Track enough information to give a good error message about one failure.
+   * Let failed symbol load override failed library open, and let failed
+   * library open override missing static symbols.
+   */
+  const char* failed_libname = NULL;
+  const char* failed_funcname = NULL;
+
+#ifdef HAVE_LIBLTDL
+  if (library_names && library_names[0])
+  {
+    const char* const* libname;
+    if (lt_dlinit())
+    {
+      lsx_fail(
+        "Unable to load %s - failed to initialize ltdl.",
+        library_description);
+      return 1;
+    }
+
+    for (libname = library_names; *libname; libname++)
+    {
+      dl = lt_dlopenext(*libname);
+      if (dl)
+      {
+        size_t i;
+        for (i = 0; func_infos[i].name; i++)
+        {
+          union {lsx_dlptr fn; lt_ptr ptr;} func;
+          func.ptr = lt_dlsym(dl, func_infos[i].name);
+          selected_funcs[i] = func.fn ? func.fn : func_infos[i].stub_func;
+          if (!selected_funcs[i])
+          {
+            lt_dlclose(dl);
+            dl = NULL;
+            failed_libname = *libname;
+            failed_funcname = func_infos[i].name;
+            break;
+          }
+        }
+
+        if (dl)
+          break;
+      }
+      else if (!failed_libname)
+      {
+        failed_libname = *libname;
+      }
+    }
+
+    if (!dl)
+      lt_dlexit();
+  }
+#endif /* HAVE_LIBLTDL */
+
+  if (!dl)
+  {
+    size_t i;
+    for (i = 0; func_infos[i].name; i++)
+    {
+      selected_funcs[i] =
+          func_infos[i].static_func
+          ? func_infos[i].static_func
+          : func_infos[i].stub_func;
+      if (!selected_funcs[i])
+      {
+        if (!failed_libname)
+        {
+          failed_libname = "static";
+          failed_funcname = func_infos[i].name;
+        }
+
+        failed = 1;
+        break;
+      }
+    }
+  }
+
+  if (failed)
+  {
+    size_t i;
+    for (i = 0; func_infos[i].name; i++)
+      selected_funcs[i] = NULL;
+#ifdef HAVE_LIBLTDL
+#define LTDL_MISSING ""
+#else
+#define LTDL_MISSING " (Dynamic library support not configured.)"
+#endif /* HAVE_LIBLTDL */
+    if (failed_funcname)
+    {
+      lsx_fail(
+        "Unable to load %s (%s) function \"%s\"." LTDL_MISSING,
+        library_description,
+        failed_libname,
+        failed_funcname);
+    }
+    else if (failed_libname)
+    {
+      lsx_fail(
+        "Unable to load %s (%s)." LTDL_MISSING,
+        library_description,
+        failed_libname);
+    }
+    else
+    {
+      lsx_fail(
+        "Unable to load %s - no dynamic library names selected." LTDL_MISSING,
+        library_description);
+    }
+  }
+
+  *pdl = dl;
+  return failed;
+}
+
+void lsx_close_dllibrary(
+  lsx_dlhandle dl UNUSED)
+{
+#ifdef HAVE_LIBLTDL
+  if (dl)
+  {
+    lt_dlclose(dl);
+    lt_dlexit();
+  }
+#endif /* HAVE_LIBLTDL */
+}
--- a/src/util.h
+++ b/src/util.h
@@ -121,3 +121,8 @@
 
 #define dB_to_linear(x) exp((x) * M_LN10 * 0.05)
 #define linear_to_dB(x) (log10(x) * 20)
+
+#ifndef HAVE_STRCASECMP
+#define strcasecmp(s1, s2) lsx_strcasecmp((s1), (s2))
+#define strncasecmp(s1, s2, n) lsx_strncasecmp((s1), (s2), (n))
+#endif