shithub: sox

Download patch

ref: a2656d44437644e9101a50566443c0d3ee362c31
parent: b6e5c588ee3c300324a9e464716ed880b9b7e01a
author: Paul Kelly <paul@stjohnspoint.co.uk>
date: Mon Jan 9 08:49:23 EST 2012

Add support for MP2 encoding using Twolame UPDATED

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -124,6 +124,7 @@
 if (NOT HAVE_LAME_LAME_H)
   optional(HAVE_LAME_LAME_H lame.h mp3lame lame_get_lametag_frame mp3)
 endif (NOT HAVE_LAME_LAME_H)
+optional(HAVE_TWOLAME_H twolame.h twolame twolame_init mp3)
 optional(HAVE_MAGIC magic.h magic magic_open "")
 #optional(HAVE_OGG_SPEEX speex/speex.h speex speex_decoder_init speex)
 optional2(HAVE_PNG png.h png png_set_rows z uncompress spectrogram)
--- a/ChangeLog
+++ b/ChangeLog
@@ -48,6 +48,7 @@
   o Add support for floating point encodings in AIFF-C files. (Ulrich Klauer)
   o Pad WAV data chunk to an even number of bytes (as required by the
     specification). [3203418] (Ulrich Klauer)
+  o Add optional MP2 write support with twolame library. (Paul Kelly)
 
 Audio device drivers:
 
--- a/FEATURES.in
+++ b/FEATURES.in
@@ -20,7 +20,7 @@
 * Macintosh HCOM files
 * Amiga MAUD files
 * AMR-WB & AMR-NB (with optional libamrwb & libamrnb libraries)
-* MP3 (with optional libmad and libmp3lame libraries)
+* MP2/MP3 (with optional libmad, libtwolame and libmp3lame libraries)
 * MP4, AAC, AC3, WAVPACK, AMR-NB files (with optional ffmpeg library)
 * AVI, WMV, Ogg Theora, MPEG video files (with optional ffmpeg library)
 (:cell:)
--- a/INSTALL
+++ b/INSTALL
@@ -25,6 +25,7 @@
 FLAC              http://flac.sourceforge.net           BSD
 LADSPA            http://www.ladspa.org                 LGPL + plugins' licence
 Lame MP3 encoder  http://lame.sourceforge.net           LGPL
+Twolame MP2 enc.  http://www.twolame.org                LGPL
 libltdl           http://www.gnu.org/software/libtool   LGPL
 MAD MP3 decoder   http://www.underbit.com/products/mad  GPL
 MP3 ID3 tags      http://www.underbit.com/products/mad  GPL
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@
  o New trim effect with enhanced capabilities.
  o Fix -p option on Windows.
  o Improved large file support.
+ o MP2 write support.
 
 For the complete list of changes, see the ChangeLog at
   http://sox.git.sourceforge.net/git/gitweb.cgi?p=sox/sox;a=blob;f=ChangeLog
--- a/configure.ac
+++ b/configure.ac
@@ -365,6 +365,32 @@
     fi
 fi
 
+
+
+dnl Check for Twolame library
+AC_ARG_WITH(twolame,
+    AC_HELP_STRING([--without-twolame],
+        [Don't try to use Twolame (MP2 Audio Encoder)]))
+using_twolame=no
+if test "$with_twolame" != "no"; then
+    using_twolame=yes
+    AC_CHECK_HEADERS(twolame.h,, using_twolame=no)
+    AC_MSG_CHECKING([whether to dlopen twolame])
+    AC_ARG_ENABLE(dl_twolame,
+      AC_HELP_STRING([--enable-dl-twolame], [Dlopen twolame instead of linking in.]),
+      enable_dl_twolame=$enableval, enable_dl_twolame=no)
+    AC_MSG_RESULT($enable_dl_twolame)
+    if test "x$using_libltdl" = "xyes" -a "x$enable_dl_twolame" = "xyes"; then
+      AC_DEFINE(DL_TWOLAME, 1, [Define to dlopen() libtwolame.])
+    else
+      enable_dl_twolame="no"
+      AC_CHECK_LIB(twolame, twolame_init, MP3_LIBS="$MP3_LIBS -ltwolame",using_twolame=no)
+      if test "$with_twolame" = "yes" -a "$using_twolame" = "no"; then
+        AC_MSG_FAILURE([cannot find libtwolame])
+      fi
+    fi
+fi
+
 # Check for libgsm
 found_libgsm=yes
 AC_CHECK_HEADERS(gsm/gsm.h, ,
@@ -529,9 +555,9 @@
 
 
 
-# MP3 format depends on libmad || LAME
+# MP2/MP3 format depends on libmad || LAME || twolame
 AC_OPTIONAL_FORMAT(mp3, MP3, [
-  if test "$using_mad" != yes -a "$using_lame" != yes; then
+  if test "$using_mad" != yes -a "$using_lame" != yes -a "$using_twolame" != yes; then
     using_mp3=no
   fi])
 
@@ -634,7 +660,7 @@
 echo "flac.......................$using_flac"
 echo "gsm........................$using_gsm $gsm_option"
 echo "lpc10......................$using_lpc10 $lpc10_option"
-echo "mp3........................$using_mp3"
+echo "mp2/mp3....................$using_mp3"
 echo " id3tag....................$using_id3tag"
 echo " lame......................$using_lame"
 if test "x$using_lame" = "xyes"; then
@@ -646,6 +672,10 @@
 echo " mad.......................$using_mad"
 if test "x$using_mad" = "xyes"; then
 echo " dlopen mad................$enable_dl_mad"
+fi
+echo " twolame...................$using_twolame"
+if test "x$using_twolame" = "xyes"; then
+echo " dlopen twolame............$enable_dl_twolame"
 fi
 echo "oggvorbis..................$using_oggvorbis"
 echo "sndfile....................$using_sndfile"
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -1,7 +1,8 @@
 /* MP3 support for SoX
  *
  * Uses libmad for MP3 decoding
- * and libmp3lame for MP3 encoding
+ * libmp3lame for MP3 encoding
+ * and libtwolame for MP2 encoding
  *
  * Written by Fabrizio Gennari <fabrizio.ge@tiscali.it>
  *
@@ -16,8 +17,12 @@
 #define HAVE_LAME 1
 #endif
 
-#if defined(HAVE_MAD_H) || defined(HAVE_LAME)
+#if defined(HAVE_TWOLAME_H) || defined(DL_TWOLAME)
+  #define HAVE_TWOLAME 1
+#endif
 
+#if defined(HAVE_MAD_H) || defined(HAVE_LAME) || defined(HAVE_TWOLAME)
+
 #ifdef HAVE_MAD_H
 #include <mad.h>
 #endif
@@ -49,6 +54,10 @@
   #define ID3_TAG_FLAG_FOOTERPRESENT 0x10
 #endif
 
+#ifdef HAVE_TWOLAME_H
+  #include <twolame.h>
+#endif
+
 #ifndef HAVE_LIBLTDL
   #undef DL_LAME
   #undef DL_MAD
@@ -177,6 +186,33 @@
   LAME_FUNC_ID3(f,x, size_t, lame_get_id3v2_tag, (lame_global_flags *, unsigned char*, size_t)) \
   LAME_FUNC_ID3(f,x, int, id3tag_set_fieldvalue, (lame_global_flags *, const char *))
 
+static const char* const twolame_library_names[] =
+{
+#ifdef DL_TWOLAME
+  "libtwolame",
+  "libtwolame-0",
+#endif
+  NULL
+};
+
+#ifdef DL_TWOLAME
+  #define TWOLAME_FUNC LSX_DLENTRY_DYNAMIC
+#else
+  #define TWOLAME_FUNC LSX_DLENTRY_STATIC
+#endif
+
+#define TWOLAME_FUNC_ENTRIES(f,x) \
+  TWOLAME_FUNC(f,x, twolame_options*, twolame_init, (void)) \
+  TWOLAME_FUNC(f,x, int, twolame_get_num_channels, (twolame_options*)) \
+  TWOLAME_FUNC(f,x, int, twolame_set_num_channels, (twolame_options*, int)) \
+  TWOLAME_FUNC(f,x, int, twolame_set_in_samplerate, (twolame_options *, int)) \
+  TWOLAME_FUNC(f,x, int, twolame_set_out_samplerate, (twolame_options *, int)) \
+  TWOLAME_FUNC(f,x, int, twolame_set_brate, (twolame_options *, int)) \
+  TWOLAME_FUNC(f,x, int, twolame_init_params, (twolame_options *)) \
+  TWOLAME_FUNC(f,x, int, twolame_encode_buffer_float32_interleaved, (twolame_options *, const float [], int, unsigned char *, int)) \
+  TWOLAME_FUNC(f,x, int, twolame_encode_flush, (twolame_options *, unsigned char *, int)) \
+  TWOLAME_FUNC(f,x, void, twolame_close, (twolame_options **))
+
 /* Private data */
 typedef struct mp3_priv_t {
   unsigned char *mp3_buffer;
@@ -192,14 +228,23 @@
   LSX_DLENTRIES_TO_PTRS(MAD_FUNC_ENTRIES, mad_dl);
 #endif /*HAVE_MAD_H*/
 
-#ifdef HAVE_LAME
-  lame_global_flags *gfp;
+#if defined(HAVE_LAME) || defined(HAVE_TWOLAME)
   float *pcm_buffer;
   size_t pcm_buffer_size;
+  char mp2;
+#endif
+
+#ifdef HAVE_LAME
+  lame_global_flags *gfp;
   uint64_t num_samples;
   int vbr_tag;
   LSX_DLENTRIES_TO_PTRS(LAME_FUNC_ENTRIES, lame_dl);
-#endif /*HAVE_LAME*/
+#endif
+
+#ifdef HAVE_TWOLAME
+  twolame_options *opt;
+  LSX_DLENTRIES_TO_PTRS(TWOLAME_FUNC_ENTRIES, twolame_dl);
+#endif
 } priv_t;
 
 #ifdef HAVE_MAD_H
@@ -616,7 +661,7 @@
 
   return SOX_EOF;
 }
-#else /*HAVE_MAD_H*/
+#else /* !HAVE_MAD_H */
 static int startread(sox_format_t * ft)
 {
   lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP3 decoding support");
@@ -828,6 +873,10 @@
   }
 }
 
+#endif /* HAVE_LAME */
+
+#if defined(HAVE_LAME) || defined(HAVE_TWOLAME)
+
 #define LAME_BUFFER_SIZE(num_samples) (((num_samples) + 3) / 4 * 5 + 7200)
 
 static int startwrite(sox_format_t * ft)
@@ -834,23 +883,47 @@
 {
   priv_t *p = (priv_t *) ft->priv;
   int openlibrary_result;
+  int fail = 0;
 
-  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");
+      lsx_report("Encoding forced to MP2/MP3");
     ft->encoding.encoding = SOX_ENCODING_MP3;
   }
 
+  if(strchr(ft->filetype, '2'))
+      p->mp2 = 1;
+
+  if (p->mp2) {
+#ifdef HAVE_TWOLAME
+    LSX_DLLIBRARY_OPEN(
+        p,
+        twolame_dl,
+        TWOLAME_FUNC_ENTRIES,
+        "Twolame encoder library",
+        twolame_library_names,
+        openlibrary_result);
+#else
+    lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP2 encoding support");
+    return SOX_EOF;
+#endif
+  } else {
+#ifdef HAVE_LAME
+    LSX_DLLIBRARY_OPEN(
+        p,
+        lame_dl,
+        LAME_FUNC_ENTRIES,
+        "LAME encoder library",
+        lame_library_names,
+        openlibrary_result);
+#else
+    lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP3 encoding support");
+    return SOX_EOF;
+#endif
+  }
+  if (openlibrary_result)
+    return SOX_EOF;
+
   p->mp3_buffer_size = LAME_BUFFER_SIZE(sox_globals.bufsiz / max(ft->signal.channels, 1));
   p->mp3_buffer = lsx_malloc(p->mp3_buffer_size);
 
@@ -857,39 +930,82 @@
   p->pcm_buffer_size = sox_globals.bufsiz * sizeof(float);
   p->pcm_buffer = lsx_malloc(p->pcm_buffer_size);
 
-  p->gfp = p->lame_init();
+  if (p->mp2) {
+#ifdef HAVE_TWOLAME
+    p->opt = p->twolame_init();
 
-  if (p->gfp == NULL){
-    lsx_fail_errno(ft,SOX_EOF,"Initialization of LAME library failed");
-    return(SOX_EOF);
-  }
+    if (p->opt == NULL){
+      lsx_fail_errno(ft,SOX_EOF,"Initialization of Twolame library failed");
+      return(SOX_EOF);
+    }
+#endif
+  } else {
+#ifdef HAVE_LAME
+    p->gfp = p->lame_init();
 
-  /* First set message callbacks so we don't miss any messages: */
-  p->lame_set_errorf(p->gfp,errorf);
-  p->lame_set_debugf(p->gfp,debugf);
-  p->lame_set_msgf  (p->gfp,msgf);
+    if (p->gfp == NULL){
+      lsx_fail_errno(ft,SOX_EOF,"Initialization of LAME library failed");
+      return(SOX_EOF);
+    }
 
-  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 > ULONG_MAX ? 0 : (unsigned long)p->num_samples);
+    /* First set message callbacks so we don't miss any messages: */
+    p->lame_set_errorf(p->gfp,errorf);
+    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 > ULONG_MAX ? 0 : (unsigned long)p->num_samples);
+#endif
+  }
+
   ft->signal.precision = MP3_LAME_PRECISION;
 
   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");
-        return(SOX_EOF);
+    if (p->mp2) {
+#ifdef HAVE_TWOLAME
+      fail = (p->twolame_set_num_channels(p->opt,(int)ft->signal.channels) != 0);
+#endif
+    } else {
+#ifdef HAVE_LAME
+      fail = (p->lame_set_num_channels(p->gfp,(int)ft->signal.channels) < 0);
+#endif
     }
+    if (fail) {
+      lsx_fail_errno(ft,SOX_EOF,"Unsupported number of channels");
+      return(SOX_EOF);
+    }
   }
-  else
-    ft->signal.channels = p->lame_get_num_channels(p->gfp); /* LAME default */
+  else {
+    if (p->mp2) {
+#ifdef HAVE_TWOLAME
+      ft->signal.channels = p->twolame_get_num_channels(p->opt); /* Twolame default */
+#endif
+    } else {
+#ifdef HAVE_LAME
+      ft->signal.channels = p->lame_get_num_channels(p->gfp); /* LAME default */
+#endif
+    }
+  }
 
-  p->lame_set_in_samplerate(p->gfp,(int)ft->signal.rate);
-  p->lame_set_out_samplerate(p->gfp,(int)ft->signal.rate);
+  if (p->mp2) {
+#ifdef HAVE_TWOLAME
+    p->twolame_set_in_samplerate(p->opt,(int)ft->signal.rate);
+    p->twolame_set_out_samplerate(p->opt,(int)ft->signal.rate);
+#endif
+  } else {
+#ifdef HAVE_LAME
+    p->lame_set_in_samplerate(p->gfp,(int)ft->signal.rate);
+    p->lame_set_out_samplerate(p->gfp,(int)ft->signal.rate);
+#endif
+  }
 
+  if (!p->mp2) {
+#ifdef HAVE_LAME
+    if (!LSX_DLFUNC_IS_STUB(p, id3tag_init))
+      write_comments(ft);
+#endif
+  }
 
-  if (!LSX_DLFUNC_IS_STUB(p, id3tag_init))
-    write_comments(ft);
-
   /* 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).
@@ -929,7 +1045,7 @@
 
   if (ft->encoding.compression == HUGE_VAL) {
     /* Do nothing, use defaults: */
-    lsx_report("using MP3 encoding defaults");
+    lsx_report("using %s encoding defaults", p->mp2? "MP2" : "MP3");
   } else {
     double abs_compression = fabs(ft->encoding.compression);
     double floor_compression = floor(abs_compression);
@@ -941,6 +1057,11 @@
         : (int)(fraction_compression * 10.0 + 0.5);
 
     if (ft->encoding.compression < 0.5) {
+      if (p->mp2) {
+        lsx_fail_errno(ft,SOX_EOF,"Variable bitrate encoding not supported for MP2 audio");
+        return(SOX_EOF);
+      }
+#ifdef HAVE_LAME
       if (p->lame_get_VBR(p->gfp) == vbr_off)
         p->lame_set_VBR(p->gfp, vbr_default);
 
@@ -958,21 +1079,32 @@
         return(SOX_EOF);
       }
       lsx_report("lame_set_VBR_q(%d)", bitrate_q);
+#endif
     } else {
-      if (p->lame_set_brate(p->gfp, bitrate_q) < 0) {
+      if (p->mp2) {
+#ifdef HAVE_TWOLAME
+        fail = (p->twolame_set_brate(p->opt, bitrate_q) != 0);
+#endif
+      } else {
+#ifdef HAVE_LAME
+        fail = (p->lame_set_brate(p->gfp, bitrate_q) < 0);
+#endif
+      }
+      if (fail) {
         lsx_fail_errno(ft, SOX_EOF,
-          "lame_set_brate(%d) failed", bitrate_q);
+          "%slame_set_brate(%d) failed", p->mp2? "two" : "", bitrate_q);
         return(SOX_EOF);
       }
-      lsx_report("lame_set_brate(%d)", bitrate_q);
+      lsx_report("(two)lame_set_brate(%d)", bitrate_q);
     }
 
     /* Set Quality */
 
-    if (encoder_q < 0) {
+    if (encoder_q < 0 || p->mp2) {
       /* use default quality value */
-      lsx_report("using MP3 default quality");
+      lsx_report("using %s default quality", p->mp2? "MP2" : "MP3");
     } else {
+#ifdef HAVE_LAME
       if (p->lame_set_quality(p->gfp, encoder_q) < 0) {
         lsx_fail_errno(ft, SOX_EOF,
           "lame_set_quality(%d) failed", encoder_q);
@@ -979,15 +1111,29 @@
         return(SOX_EOF);
       }
       lsx_report("lame_set_quality(%d)", encoder_q);
+#endif
     }
   }
 
-  p->lame_set_bWriteVbrTag(p->gfp, p->vbr_tag);
+  if (!p->mp2) {
+#ifdef HAVE_LAME
+    p->lame_set_bWriteVbrTag(p->gfp, p->vbr_tag);
+#endif
+  }
 
-  if (p->lame_init_params(p->gfp) < 0){
-        lsx_fail_errno(ft,SOX_EOF,"LAME initialization failed");
-        return(SOX_EOF);
+  if (p->mp2) {
+#ifdef HAVE_TWOLAME
+    fail = (p->twolame_init_params(p->opt) != 0);
+#endif
+  } else {
+#ifdef HAVE_LAME
+    fail = (p->lame_init_params(p->gfp) < 0);
+#endif
   }
+  if (fail) {
+    lsx_fail_errno(ft,SOX_EOF,"%s initialization failed", p->mp2? "Twolame" : "LAME");
+    return(SOX_EOF);
+  }
 
   return(SOX_SUCCESS);
 }
@@ -1001,7 +1147,7 @@
     float *buffer_l, *buffer_r = NULL;
     int nsamples = samp/ft->signal.channels;
     int i,j;
-    size_t written;
+    int written = 0;
     int clips = 0;
     SOX_SAMPLE_LOCALS;
 
@@ -1018,25 +1164,34 @@
 
     buffer_l = p->pcm_buffer;
 
-    if (ft->signal.channels == 2)
+    if (p->mp2)
     {
-        /* lame doesn't support interleaved samples for floats so we must break
-         * them out into seperate buffers.
-         */
-        buffer_r = p->pcm_buffer + nsamples;
-        j=0;
-        for (i = 0; i < nsamples; i++)
-        {
-            buffer_l[i] = MP3_SAMPLE_TO_FLOAT(buf[j++], clips);
-            buffer_r[i] = MP3_SAMPLE_TO_FLOAT(buf[j++], clips);
-        }
+        size_t s;
+        for(s = 0; s < samp; s++)
+            buffer_l[s] = SOX_SAMPLE_TO_FLOAT_32BIT(buf[s], clips);
     }
     else
     {
-        j=0;
-        for (i = 0; i < nsamples; i++) {
-            buffer_l[i] = MP3_SAMPLE_TO_FLOAT(buf[j++], clips);
+        if (ft->signal.channels == 2)
+        {
+            /* lame doesn't support interleaved samples for floats so we must break
+             * them out into seperate buffers.
+             */
+            buffer_r = p->pcm_buffer + nsamples;
+            j=0;
+            for (i = 0; i < nsamples; i++)
+            {
+                buffer_l[i] = MP3_SAMPLE_TO_FLOAT(buf[j++], clips);
+                buffer_r[i] = MP3_SAMPLE_TO_FLOAT(buf[j++], clips);
+            }
         }
+        else
+        {
+            j=0;
+            for (i = 0; i < nsamples; i++) {
+                buffer_l[i] = MP3_SAMPLE_TO_FLOAT(buf[j++], clips);
+            }
+        }
     }
 
     new_buffer_size = LAME_BUFFER_SIZE(nsamples);
@@ -1050,15 +1205,23 @@
       p->mp3_buffer = new_buffer;
     }
 
-    if ((written =
-      p->lame_encode_buffer_float(p->gfp, buffer_l, buffer_r,
-                   nsamples, p->mp3_buffer,
-                   (int)p->mp3_buffer_size)) > p->mp3_buffer_size){
+    if(p->mp2) {
+#ifdef HAVE_TWOLAME
+        written = p->twolame_encode_buffer_float32_interleaved(p->opt, buffer_l,
+                  nsamples, p->mp3_buffer, (int)p->mp3_buffer_size);
+#endif
+    } else {
+#ifdef HAVE_LAME
+        written = p->lame_encode_buffer_float(p->gfp, buffer_l, buffer_r,
+                  nsamples, p->mp3_buffer, (int)p->mp3_buffer_size);
+#endif
+    }
+    if (written < 0) {
         lsx_fail_errno(ft,SOX_EOF,"Encoding failed");
         return 0;
     }
 
-    if (lsx_writebuf(ft, p->mp3_buffer, written) < written)
+    if (lsx_writebuf(ft, p->mp3_buffer, (size_t)written) < (size_t)written)
     {
         lsx_fail_errno(ft,SOX_EOF,"File write failed");
         return 0;
@@ -1071,31 +1234,54 @@
 {
   priv_t *p = (priv_t *) ft->priv;
   uint64_t 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);
+  int written = 0;
 
+  if (p->mp2) {
+#ifdef HAVE_TWOLAME
+    written = p->twolame_encode_flush(p->opt, p->mp3_buffer, (int)p->mp3_buffer_size);
+#endif
+  } else {
+#ifdef HAVE_LAME
+    written = p->lame_encode_flush(p->gfp, p->mp3_buffer, (int)p->mp3_buffer_size);
+#endif
+  }
   if (written < 0)
     lsx_fail_errno(ft, SOX_EOF, "Encoding failed");
   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 (ft->seekable && (num_samples != p->num_samples || p->vbr_tag))
-    rewrite_tags(ft, num_samples);
+  else if (!p->mp2) {
+#ifdef HAVE_LAME
+    if (ft->seekable && (num_samples != p->num_samples || p->vbr_tag))
+      rewrite_tags(ft, num_samples);
+#endif
+  }
 
   free(p->mp3_buffer);
   free(p->pcm_buffer);
-  p->lame_close(p->gfp);
-  LSX_DLLIBRARY_CLOSE(p, lame_dl);
+
+  if(p->mp2) {
+#ifdef HAVE_TWOLAME
+    p->twolame_close(&p->opt);
+    LSX_DLLIBRARY_CLOSE(p, twolame_dl);
+#endif
+  } else {
+#ifdef HAVE_LAME
+    p->lame_close(p->gfp);
+    LSX_DLLIBRARY_CLOSE(p, lame_dl);
+#endif
+  }
   return SOX_SUCCESS;
 }
 
-#else /* HAVE_LAME */
+#else /* !(HAVE_LAME || HAVE_TWOLAME) */
 static int startwrite(sox_format_t * ft UNUSED)
 {
-  lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP3 encoding support");
+  lsx_fail_errno(ft,SOX_EOF,"SoX was compiled with neither MP2 nor MP3 encoding support");
   return SOX_EOF;
 }
 #define sox_mp3write NULL
 #define stopwrite NULL
-#endif /* HAVE_LAME */
+#endif /* HAVE_LAME || HAVE_TWOLAME */
 
 LSX_FORMAT_HANDLER(mp3)
 {
@@ -1102,12 +1288,14 @@
   static char const * const names[] = {"mp3", "mp2", "audio/mpeg", NULL};
   static unsigned const write_encodings[] = {
     SOX_ENCODING_MP3, 0, 0};
+  static sox_rate_t const write_rates[] = {
+    8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 0};
   static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
-    "MPEG Layer 3 lossy audio compression", names, 0,
+    "MPEG Layer 2/3 lossy audio compression", names, 0,
     startread, sox_mp3read, stopread,
     startwrite, sox_mp3write, stopwrite,
-    sox_mp3seek, write_encodings, NULL, sizeof(priv_t)
+    sox_mp3seek, write_encodings, write_rates, sizeof(priv_t)
   };
   return &handler;
 }
-#endif /* defined(HAVE_MAD_H) || defined(HAVE_LAME) */
+#endif /* defined(HAVE_MAD_H) || defined(HAVE_LAME) || defined(HAVE_TWOLAME) */