/* Copyright (C) 2002 Fabrizio Gennari <fabrizio.ge@tiscali.it>
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

/* The decoding part is based on the decoder-tutorial program madlld
 * which is Copyright (c) 2001-2004 Bertrand Petit <madlld@phoe.fmug.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 * 
 * 3. Neither the name of the author nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* MP3 support for SoX
 *
 * Uses libmad for MP3 decoding
 * libmp3lame for MP3 encoding
 * and libtwolame for MP2 encoding
 */

#include "sox_i.h"

#if defined(HAVE_MAD) || defined(HAVE_LAME) || defined(HAVE_TWOLAME)

#ifdef HAVE_MAD_H
#include <mad.h>
#elif defined(DL_MAD)
#include "bit-rot/mad.h"
#endif

#if defined(HAVE_LAME_LAME_H)
#include <lame/lame.h>
#elif defined(HAVE_LAME_H)
#include <lame.h>
#elif defined(DL_LAME)
typedef struct lame_global_struct lame_global_flags;
typedef enum {
  vbr_off=0,
  vbr_default=4
} vbr_mode;
#endif

#if defined(HAVE_ID3TAG) && (defined(HAVE_IO_H) || defined(HAVE_UNISTD_H))
#define USING_ID3TAG 1
#endif

#ifdef USING_ID3TAG
  #include <id3tag.h>
  #include "id3.h"
#if defined(HAVE_UNISTD_H)
  #include <unistd.h>
#elif defined(HAVE_IO_H)
  #include <io.h>
#endif
#else
  #define ID3_TAG_FLAG_FOOTERPRESENT 0x10
#endif

#ifdef HAVE_TWOLAME_H
#include <twolame.h>
#elif defined(DL_LAME)
typedef struct twolame_options_struct twolame_options;
#endif

#ifndef HAVE_LIBLTDL
  #undef DL_MAD
  #undef DL_LAME
  #undef DL_TWOLAME
#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.
 */
#ifdef HAVE_MAD
#define mad_timer_zero mad_timer_zero_stub
static mad_timer_t const mad_timer_zero_stub = {0, 0};
#endif

#define MAXFRAMESIZE 2880
#define ID3PADDING 128

/* Twolame takes float values as input. */
#define MP2_TWOLAME_PRECISION   24

/* LAME takes float values as input. */
#define MP3_LAME_PRECISION   24

/* Note: sox_precision() returns 0 for SOX_ENCODING_MP2 and _MP3, as it varies
 * according to whether you're encoding or decoding but sox_ng.c knows
 * about this and has a special case and reports 24 as the Writes: precision */

/* MAD returns values with MAD_F_FRACBITS (28) bits of precision, though it's
   not certain that all of them are meaningful. Default to 16 bits to
   align with most users expectation of output file should be 16 bits. */
#define MP3_MAD_PRECISION    16

#ifdef HAVE_MAD
static const char* const mad_library_names[] =
{
#ifdef DL_MAD
    "libmad",
    "libmad-0",
    "cygmad-0",
#endif
    NULL
};

#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))
#endif /* HAVE_MAD */

#if HAVE_LAME
static const char* const lame_library_names[] =
{
#ifdef DL_LAME
  "libmp3lame",
  "libmp3lame-0",
  "lame-enc",
  "cygmp3lame-0",
#endif
  NULL
};
#endif

#ifdef DL_LAME

  /* Expected to be present in all builds of LAME. */
  #define LAME_FUNC           LSX_DLENTRY_DYNAMIC

  /* id3tag support is an optional component of LAME. Use if available. */
  #define LAME_FUNC_ID3       LSX_DLENTRY_STUB

#else /* DL_LAME */

  /* Expected to be present in all builds of LAME. */
  #define LAME_FUNC           LSX_DLENTRY_STATIC

  /* id3tag support is an optional component of LAME. Use if available. */
  #ifdef HAVE_LAME_ID3TAG
    #define LAME_FUNC_ID3     LSX_DLENTRY_STATIC
  #else
    #define LAME_FUNC_ID3     LSX_DLENTRY_STUB
  #endif

#endif /* DL_LAME */

#define LAME_FUNC_ENTRIES(f,x) \
  LAME_FUNC(f,x, lame_global_flags*, lame_init, (void)) \
  LAME_FUNC(f,x, int, lame_set_errorf, (lame_global_flags *, void (*)(const char *, va_list))) \
  LAME_FUNC(f,x, int, lame_set_debugf, (lame_global_flags *, void (*)(const char *, va_list))) \
  LAME_FUNC(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_float, (lame_global_flags *, const float[], const float[], 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(f,x, size_t, lame_get_lametag_frame, (const 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_title, (lame_global_flags *, const char* title)) \
  LAME_FUNC_ID3(f,x, void, id3tag_set_artist, (lame_global_flags *, const char* artist)) \
  LAME_FUNC_ID3(f,x, void, id3tag_set_album, (lame_global_flags *, const char* album)) \
  LAME_FUNC_ID3(f,x, void, id3tag_set_year, (lame_global_flags *, const char* year)) \
  LAME_FUNC_ID3(f,x, void, id3tag_set_comment, (lame_global_flags *, const char* comment)) \
  LAME_FUNC_ID3(f,x, int, id3tag_set_track, (lame_global_flags *, const char* track)) \
  LAME_FUNC_ID3(f,x, int, id3tag_set_genre, (lame_global_flags *, const char* genre)) \
  LAME_FUNC_ID3(f,x, size_t, id3tag_set_pad, (lame_global_flags *, 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, int, id3tag_set_fieldvalue, (lame_global_flags *, const char *))

#ifdef HAVE_TWOLAME
static const char* const twolame_library_names[] =
{
#ifdef DL_TWOLAME
  "libtwolame",
  "libtwolame-0",
#endif
  NULL
};
#endif /* HAVE_TWOLAME */

#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_set_verbosity, (twolame_options*, int)) \
  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_set_VBR, (twolame_options *, int)) \
  TWOLAME_FUNC(f,x, int, twolame_set_VBR_level, (twolame_options *, float)) \
  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;
  size_t mp3_buffer_size;

#ifdef HAVE_MAD
  struct mad_stream       Stream;
  struct mad_frame        Frame;
  struct mad_synth        Synth;
  mad_timer_t             Timer;
  ptrdiff_t               cursamp;
  size_t                  FrameCount;
  LSX_DLENTRIES_TO_PTRS(MAD_FUNC_ENTRIES, mad_dl);
#endif /* HAVE_MAD */

#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

#ifdef HAVE_TWOLAME
  twolame_options *opt;
  LSX_DLENTRIES_TO_PTRS(TWOLAME_FUNC_ENTRIES, twolame_dl);
#endif
} priv_t;

#ifdef HAVE_MAD 

/* This function merges the functions tagtype() and id3_tag_query()
   from MAD's libid3tag, so we don't have to link to it
   Returns 0 if the frame is not an ID3 tag, tag length if it is */

static int tagtype(const unsigned char *data, size_t length)
{
    if (length >= 3 && data[0] == 'T' && data[1] == 'A' && data[2] == 'G')
    {
        return 128; /* ID3V1 */
    }

    if (length >= 10 &&
        (data[0] == 'I' && data[1] == 'D' && data[2] == '3') &&
        data[3] < 0xff && data[4] < 0xff &&
        data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80)
    {     /* ID3V2 */
        unsigned char flags;
        unsigned int size;
        flags = data[5];
        size = 10 + (data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9];
        if (flags & ID3_TAG_FLAG_FOOTERPRESENT)
            size += 10;
        for (; size < length && !data[size]; ++size);  /* Consume padding */
        return size;
    }

    return 0;
}

#endif /* HAVE_MAD */

#include "mp3-util.h"

#ifdef HAVE_MAD

/*
 * (Re)fill the stream buffer that is to be decoded.  If any data
 * still exists in the buffer then they are first shifted to be
 * front of the stream buffer.
 */
static int sox_mad_input(sox_format_t * ft)
{
    priv_t *p = (priv_t *) ft->priv;
    size_t bytes_read;
    size_t remaining;

    remaining = p->Stream.bufend - p->Stream.next_frame;

    /* libmad does not consume all the buffer it's given. Some
     * data, part of a truncated frame, is left unused at the
     * end of the buffer. That data must be put back at the
     * beginning of the buffer and taken in account for
     * refilling the buffer. This means that the input buffer
     * must be large enough to hold a complete frame at the
     * highest observable bit-rate (currently 448 kb/s).
     * TODO: Is 2016 bytes the size of the largest frame?
     * (448000*(1152/32000))/8
     */
    memmove(p->mp3_buffer, p->Stream.next_frame, 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->mp3_buffer, bytes_read+remaining);
    p->Stream.error = 0;

    return SOX_SUCCESS;
}

/* Attempts to read an ID3 tag at the current location in stream and
 * consume it all.  Returns SOX_EOF if no tag is found.  Its up to
 * caller to recover.
 * */
static int sox_mad_inputtag(sox_format_t * ft)
{
    priv_t *p = (priv_t *) ft->priv;
    int rc = SOX_EOF;
    size_t remaining;
    size_t tagsize;


    /* FIXME: This needs some more work if we are to ever
     * look at the ID3 frame.  This is because the Stream
     * may not be able to hold the complete ID3 frame.
     * We should consume the whole frame inside tagtype()
     * instead of outside of tagframe().  That would support
     * recovering when Stream contains less then 8-bytes (header)
     * and also when ID3v2 is bigger then Stream buffer size.
     * Need to pass in stream so that buffer can be
     * consumed as well as letting additional data to be
     * read in.
     */
    remaining = p->Stream.bufend - p->Stream.next_frame;
    if ((tagsize = tagtype(p->Stream.this_frame, remaining)))
    {
        p->mad_stream_skip(&p->Stream, tagsize);
        rc = SOX_SUCCESS;
    }

    /* We know that a valid frame hasn't been found yet
     * so help libmad out and go back into frame seek mode.
     * This is true whether an ID3 tag was found or not.
     */
    p->mad_stream_sync(&p->Stream);

    return rc;
}

static int startread(sox_format_t * ft)
{
  priv_t *p = (priv_t *) ft->priv;
  size_t ReadSize;
  sox_bool ignore_length = ft->signal.length == SOX_IGNORE_LENGTH;
  int open_library_result;

  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;

  p->mp3_buffer_size = sox_globals.bufsiz;
  p->mp3_buffer = lsx_malloc(p->mp3_buffer_size);

  ft->signal.length = SOX_UNSPEC;
  if (ft->seekable) {
#ifdef USING_ID3TAG
    lsx_id3_read_tag(ft, sox_true);
    lsx_rewind(ft);
    if (!ft->signal.length)
#endif
      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);

  /* 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 && lsx_error(ft))
    return SOX_EOF;

  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_mad_input(ft) == SOX_EOF)
              return SOX_EOF;

          continue;
      }

      /* Consume any ID3 tags */
      sox_mad_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;
  }

  if (p->Stream.error)
  {
      lsx_fail_errno(ft,SOX_EOF,"no valid MP3 frame found");
      return SOX_EOF;
  }

  lsx_report("MAD says it's MPEG-1 layer %d",
      p->Frame.header.layer == MAD_LAYER_I ? 1 :
      p->Frame.header.layer == MAD_LAYER_II ? 2 :
      p->Frame.header.layer == MAD_LAYER_III ? 3 :
      p->Frame.header.layer);

  switch (p->Frame.header.layer) {
  case MAD_LAYER_I:
      /* MPEG-1 Layer I is a simpler encoding at bitrates from 32 to 448 kbps
       * and sampling rates of 32, 44.1 and 48 kHz.
       * It uses a 512-point DFT as opposed to Layer 2's 1024-point DFT
       * and was used in the Philips Digital Compact Cassette.
       * MP1 is a subset of MP2 and MP2 decoders can decode MP1 */
      ft->encoding.encoding = SOX_ENCODING_MP1;
      break;
  case MAD_LAYER_II:
      ft->encoding.encoding = SOX_ENCODING_MP2;
      break;
  case MAD_LAYER_III: /* mono or stereo */
      ft->encoding.encoding = SOX_ENCODING_MP3;
      break;
  default:
      lsx_fail_errno(ft,SOX_EOF,"no valid MPEG layer 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;
  }

  p->FrameCount=1;

  p->mad_timer_add(&p->Timer,p->Frame.header.duration);
  p->mad_synth_frame(&p->Synth,&p->Frame);
  ft->signal.precision = MP3_MAD_PRECISION;
  ft->signal.rate=p->Synth.pcm.samplerate;
  if (ignore_length)
    ft->signal.length = SOX_UNSPEC;
  else {
    ft->signal.length = (uint64_t)(ft->signal.length * .001 * ft->signal.rate + .5);
    ft->signal.length *= ft->signal.channels;  /* Keep separate from line above! */
  }

  p->cursamp = 0;

  return SOX_SUCCESS;
}

/*
 * Read up to len samples from p->Synth
 * If needed, read some more MP3 data, decode them and synth them
 * Place in buf[].
 * Return number of samples read.
 */
static size_t sox_mp3read(sox_format_t * ft, sox_sample_t *buf, size_t len)
{
    priv_t *p = (priv_t *) ft->priv;
    size_t donow,i,done=0;
    mad_fixed_t sample;
    size_t chan;

    do {
        size_t x = (p->Synth.pcm.length - p->cursamp)*ft->signal.channels;
        donow=min(len, x);
        i=0;
        while(i<donow){
            for(chan=0;chan<ft->signal.channels;chan++){
                sample=p->Synth.pcm.samples[chan][p->cursamp];
                if (sample < -MAD_F_ONE)
                    sample=-MAD_F_ONE;
                else if (sample >= MAD_F_ONE)
                    sample=MAD_F_ONE-1;
                *buf++=(sox_sample_t)(sample<<(32-1-MAD_F_FRACBITS));
                i++;
            }
            p->cursamp++;
        };

        len-=donow;
        done+=donow;

        if (len==0) break;

        /* check whether input buffer needs a refill */
        if (p->Stream.error == MAD_ERROR_BUFLEN)
        {
            if (sox_mad_input(ft) == SOX_EOF) {
                lsx_debug("sox_mad_input EOF");
                break;
            }
        }

        if (p->mad_frame_decode(&p->Frame,&p->Stream))
        {
            if(MAD_RECOVERABLE(p->Stream.error))
            {
                sox_mad_inputtag(ft);
                continue;
            }
            else
            {
                if (p->Stream.error == MAD_ERROR_BUFLEN)
                    continue;
                else
                {
                    lsx_report("unrecoverable frame level error (%s)",
                              p->mad_stream_errorstr(&p->Stream));
                    break;
                }
            }
        }
        p->FrameCount++;
        p->mad_timer_add(&p->Timer,p->Frame.header.duration);
        p->mad_synth_frame(&p->Synth,&p->Frame);
        p->cursamp=0;
    } while(1);

    return done;
}

static int stopread(sox_format_t * ft)
{
  priv_t *p=(priv_t*) ft->priv;

  mad_synth_finish(&p->Synth);
  p->mad_frame_finish(&p->Frame);
  p->mad_stream_finish(&p->Stream);

  free(p->mp3_buffer);
  LSX_DLLIBRARY_CLOSE(p, mad_dl);
  return SOX_SUCCESS;
}

static int sox_mp3seek(sox_format_t * ft, sox_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 */
  lsx_rewind(ft);
  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 */
    size_t padding = 0;
    size_t read;
    size_t leftover = p->Stream.bufend - p->Stream.next_frame;

    memmove(p->mp3_buffer, p->Stream.this_frame, leftover);
    read = lsx_readbuf(ft, p->mp3_buffer + leftover, p->mp3_buffer_size - leftover);
    if (read == 0) {
      lsx_debug("seek failure. unexpected EOF (frames=%" PRIuPTR " leftover=%" PRIuPTR ")", p->FrameCount, 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 (sox_true) {  /* 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) {
              if (lsx_seeki(ft, (off_t)(tagsize - available), SEEK_CUR))
	        return SOX_EOF;
              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, (off_t)(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;
}
#endif /* HAVE_MAD */


#ifndef HAVE_MAD

#define startread NULL
#define sox_mp3read NULL
#define stopread NULL
#define sox_mp3seek NULL

#endif /* !HAVE_MAD */

#ifdef HAVE_LAME

/* Adapters for lame message callbacks: */

static void errorf(const char* fmt, va_list va)
{
  sox_globals.subsystem=__FILE__;
  if (sox_globals.output_message_handler)
    (*sox_globals.output_message_handler)(1,sox_globals.subsystem,fmt,va);
  return;
}

static void debugf(const char* fmt, va_list va)
{
  sox_globals.subsystem=__FILE__;
  if (sox_globals.output_message_handler)
    (*sox_globals.output_message_handler)(4,sox_globals.subsystem,fmt,va);
  return;
}

static void msgf(const char* fmt, va_list va)
{
  sox_globals.subsystem=__FILE__;
  if (sox_globals.output_message_handler)
    (*sox_globals.output_message_handler)(3,sox_globals.subsystem,fmt,va);
  return;
}

/* These functions are considered optional. If they aren't present in the
   library, the stub versions defined here will be used instead. */

UNUSED static void id3tag_init_stub(lame_global_flags * gfp UNUSED)
  { return; }
UNUSED static void id3tag_set_title_stub(lame_global_flags * gfp UNUSED, const char* title UNUSED)
  { return; }
UNUSED static void id3tag_set_artist_stub(lame_global_flags * gfp UNUSED, const char* artist UNUSED)
  { return; }
UNUSED static void id3tag_set_album_stub(lame_global_flags * gfp UNUSED, const char* album UNUSED)
  { return; }
UNUSED static void id3tag_set_year_stub(lame_global_flags * gfp UNUSED, const char* year UNUSED)
  { return; }
UNUSED static void id3tag_set_comment_stub(lame_global_flags * gfp UNUSED, const char* comment UNUSED)
  { return; }
UNUSED static void id3tag_set_track_stub(lame_global_flags * gfp UNUSED, const char* track UNUSED)
  { return; }
UNUSED static int id3tag_set_genre_stub(lame_global_flags * gfp UNUSED, const char* genre UNUSED)
  { return 0; }
UNUSED static size_t id3tag_set_pad_stub(lame_global_flags * gfp UNUSED, size_t n UNUSED)
  { return 0; }
UNUSED static size_t lame_get_id3v2_tag_stub(lame_global_flags * gfp UNUSED, unsigned char * buffer UNUSED, size_t size UNUSED)
  { return 0; }
UNUSED 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)
{
  size_t bytes_read;
  int id3v2_size;
  unsigned char id3v2_header[10];

  if (lsx_seeki(ft, (off_t)0, SEEK_SET) != 0) {
    lsx_warn("cannot update id3 tag - failed to seek to beginning");
    return SOX_EOF;
  }

  /* read 10 bytes in case there's an ID3 version 2 header here */
  bytes_read = lsx_readbuf(ft, id3v2_header, sizeof(id3v2_header));
  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 */
  }

  /* does the stream begin with the ID3 version 2 file identifier? */
  if (!strncmp((char *) id3v2_header, "ID3", (size_t)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, size_t id3v2_size, uint64_t num_samples)
{
  priv_t *p = (priv_t *)ft->priv;
  size_t new_size;
  unsigned char * buffer;

  if (LSX_DLFUNC_IS_STUB(p, lame_get_id3v2_tag))
  {
    if (p->num_samples) {
      lsx_warn("this version of LAME does not support tag update;");
      lsx_warn("the track length will be incorrect");
    } else {
      lsx_report("this version of LAME does not support tag update;");
      lsx_report("the track length will be unspecified");
    }
    return;
  }

  buffer = lsx_malloc(id3v2_size);

  if (num_samples > ULONG_MAX)
  {
    lsx_warn("cannot accurately update track length info - file is too long");
    num_samples = 0;
  }
  p->lame_set_num_samples(p->gfp, (unsigned long)num_samples);
  lsx_debug("updated MP3 TLEN to %lu samples", (unsigned long)num_samples);

  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) {
    if (LSX_DLFUNC_IS_STUB(p, id3tag_set_pad))
    {
      if (p->num_samples) {
        lsx_report("this version of LAME does not support tag size adjustment;");
        lsx_report("the track length will be invalid");
      } else {
        lsx_report("this version of LAME does not support tag size adjustment;");
        lsx_report("the track length will be unspecified");
      }
    }
    else
      lsx_warn("cannot update track length info - failed to adjust tag size");
  } else {
    if (lsx_seeki(ft, (off_t)0, SEEK_SET))
      lsx_warn("cannot rewrite Id3v2 tag");
      
    /* Overwrite the Id3v2 tag (this time TLEN should be accurate) */
    if (lsx_writebuf(ft, buffer, id3v2_size) != 1) {
      lsx_debug("Rewrote Id3v2 tag (%" PRIuPTR " bytes)", id3v2_size);
    }
  }

  free(buffer);
}

static void rewrite_tags(sox_format_t * ft, uint64_t num_samples)
{
  priv_t *p = (priv_t *)ft->priv;

  off_t file_size;
  size_t id3v2_size;

  if (lsx_seeki(ft, (off_t)0, SEEK_END)) {
    lsx_warn("cannot update tags - seek to end failed");
    return;
  }

  /* Get file size */
  file_size = lsx_tell(ft);

  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 && num_samples != p->num_samples) {
    rewrite_id3v2_tag(ft, id3v2_size, num_samples);
  }

  if (p->vbr_tag) {
    size_t lametag_size;
    uint8_t buffer[MAXFRAMESIZE];

    if (lsx_seeki(ft, (off_t)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 (lsx_writebuf(ft, buffer, lametag_size) != lametag_size) {
      lsx_warn("cannot write VBR tag - VBR tag write failed");
    } else {
      lsx_debug("rewrote VBR tag (%" PRIuPTR " bytes)", lametag_size);
    }
  }
}

#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_mp2(sox_format_t * ft)
{
#if !HAVE_TWOLAME
  lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP2 encoding support");
  return SOX_EOF;
#else
  priv_t *p = (priv_t *) ft->priv;
  int openlibrary_result;

  LSX_DLLIBRARY_OPEN(
      p,
      twolame_dl,
      TWOLAME_FUNC_ENTRIES,
      "Twolame encoder library",
      twolame_library_names,
      openlibrary_result);
  if (openlibrary_result)
    return SOX_EOF;

  if ((p->opt = p->twolame_init()) == NULL){
    lsx_fail_errno(ft,SOX_EOF,"initialization of Twolame library failed");
    return SOX_EOF;
  }
  (void) p->twolame_set_verbosity(p->opt, 0);

  ft->signal.precision = MP2_TWOLAME_PRECISION;

  if (ft->signal.channels != SOX_ENCODING_UNKNOWN) {
    if (p->twolame_set_num_channels(p->opt,(int)ft->signal.channels) != 0) {
      lsx_fail_errno(ft,SOX_EOF,"unsupported number of channels");
      return(SOX_EOF);
    }
  } else {
    ft->signal.channels = p->twolame_get_num_channels(p->opt); /* Twolame default */
  }

  p->twolame_set_in_samplerate(p->opt,(int)ft->signal.rate);
  p->twolame_set_out_samplerate(p->opt,(int)ft->signal.rate);

  lsx_debug("-C option is %f", ft->encoding.compression);

  if (ft->encoding.compression == HUGE_VAL) {
    /* Do nothing, use defaults: */
    lsx_report("using MP2 encoding defaults");
  } else {
    /* Twolame's CBR rates are exactly 32,48,64... and VBR rates a float
     * from -50 to +50 with "useful" values from -10 to +10 so we count
     * any floating point value from -50 to 50 as VBR unless it is exactly
     * 32 or 48, and anything else as CBR.
     */
    if (ft->encoding.compression >=-50 && ft->encoding.compression <= 50 &&
        ft->encoding.compression != 32 && ft->encoding.compression != 48) {
        if (p->twolame_set_VBR(p->opt, 1) != 0) {
          lsx_fail_errno(ft,SOX_EOF,"failed to enable MP2 VBR");
          return(SOX_EOF);
        }
        if (p->twolame_set_VBR_level(p->opt, ft->encoding.compression) != 0) {
          lsx_fail_errno(ft,SOX_EOF,"failed to set MP2 VBR level");
          return(SOX_EOF);
        }
    } else {
      if (p->twolame_set_brate(p->opt, (int)ft->encoding.compression) != 0) {
        lsx_fail_errno(ft, SOX_EOF, "invalid MP2 bitrate");
        return(SOX_EOF);
      }
      lsx_report("twolame set bitrate(%d)", (int)ft->encoding.compression);
    }
  }

  if (p->twolame_init_params(p->opt) != 0) {
    lsx_fail_errno(ft,SOX_EOF,"Twolame initialization failed");
    return(SOX_EOF);
  }

  return(SOX_SUCCESS);
#endif
}

static int startwrite_mp3(sox_format_t * ft)
{
#if !HAVE_LAME
  lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP3 encoding support");
  return SOX_EOF;
#else
  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 ((p->gfp = p->lame_init()) == NULL){
    lsx_fail_errno(ft,SOX_EOF,"initialization of LAME library failed");
    return SOX_EOF;
  }

  /* 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);

  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);
    }
  } else {
    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);

  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).
   *
   * The second most important parameter is probably "quality" (really
   * performance), which allows balancing encoding speed vs. quality.
   * In LAME, 0 specifies highest quality but is very slow, while
   * 9 selects poor quality, but is fast. (5 is the default and 2 is
   * recommended as a good trade-off for high quality encodes.)
   *
   * 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 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
   * size) is selected using a number from 0 to 9. Use a value of 0 for high
   * quality, larger files, and 9 for smaller files of lower quality. 4 is
   * the default.
   *
   * In order to squeeze the selection of VBR into the encoding.compression
   * float we use negative numbers to select VRR. -4.2 would select default
   * VBR encoding (size) with high quality (speed). One special case is 0,
   * which is a valid VBR encoding parameter but not a valid bitrate.
   * Compression value of 0 is always treated as a high quality vbr, as a
   * result both -0.2 and 0.2 are treated as highest quality VBR (size) and
   * high quality (speed).
   *
   * Note: It would have been nice to simply use low values, 0-9, to trigger
   * VBR mode, but 8 kbps is a valid bit rate, so negative values were
   * used instead.
   */

  lsx_debug("-C option is %f", ft->encoding.compression);

  if (ft->encoding.compression == HUGE_VAL) {
    /* Do nothing, use defaults: */
    lsx_report("using MP3 encoding defaults");
  } else {
    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 (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->vbr_tag = 1;
      } else {
        lsx_warn("unable to write VBR tag because we can't seek");
      }

      if (p->lame_set_VBR_q(p->gfp, bitrate_q) < 0)
      {
        lsx_fail_errno(ft, SOX_EOF,
          "lame_set_VBR_q(%d) failed (should be between 0 and 9)",
          bitrate_q);
        return(SOX_EOF);
      }
      lsx_report("lame_set_VBR_q(%d)", bitrate_q);
    } else {
      if (p->lame_set_brate(p->gfp, bitrate_q) < 0) {
        lsx_fail_errno(ft, SOX_EOF,
          "lame_set_brate(%d) failed", bitrate_q);
        return(SOX_EOF);
      }
      lsx_report("lame_set_brate(%d)", bitrate_q);
    }

    /* Set Quality */

    if (encoder_q < 0) {
      /* use default quality value */
      lsx_report("using MP3 default quality");
    } else {
      if (p->lame_set_quality(p->gfp, encoder_q) < 0) {
        lsx_fail_errno(ft, SOX_EOF,
          "lame_set_quality(%d) failed", encoder_q);
        return(SOX_EOF);
      }
      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);
  }

  return(SOX_SUCCESS);
#endif
}

static int startwrite(sox_format_t * ft)
{
  priv_t *p = (priv_t *) ft->priv;

  p->mp2 = !!strchr(ft->filetype, '2');

  if (ft->encoding.encoding != SOX_ENCODING_MP2 &&
      ft->encoding.encoding != SOX_ENCODING_MP3) {
    if(ft->encoding.encoding != SOX_ENCODING_UNKNOWN)
      lsx_report("encoding forced to MP%d", p->mp2 ? 2 : 3);
    ft->encoding.encoding = p->mp2 ? SOX_ENCODING_MP2 : SOX_ENCODING_MP3;
  }

  p->mp3_buffer_size = LAME_BUFFER_SIZE(sox_globals.bufsiz / max(ft->signal.channels, 1));
  p->mp3_buffer = lsx_malloc(p->mp3_buffer_size);

  p->pcm_buffer_size = sox_globals.bufsiz * sizeof(float);
  p->pcm_buffer = lsx_malloc(p->pcm_buffer_size);


  return p->mp2 ? startwrite_mp2(ft) : startwrite_mp3(ft);
}

#define MP3_SAMPLE_TO_FLOAT(d,clips) ((float)(32768*SOX_SAMPLE_TO_FLOAT_32BIT(d,clips)))

static size_t sox_mp3write(sox_format_t * ft, const sox_sample_t *buf, size_t samp)
{
    priv_t *p = (priv_t *)ft->priv;
    size_t new_buffer_size;
    float *buffer_l, *buffer_r = NULL;
    int nsamples = samp/ft->signal.channels;
    int i,j;
    int written = 0;
    int clips = 0;
    SOX_SAMPLE_LOCALS;

    new_buffer_size = samp * sizeof(float);
    if (p->pcm_buffer_size < new_buffer_size) {
      float *new_buffer = lsx_realloc(p->pcm_buffer, new_buffer_size);
      p->pcm_buffer_size = new_buffer_size;
      p->pcm_buffer = new_buffer;
    }

    buffer_l = p->pcm_buffer;

    if (p->mp2)
    {
        size_t s;
        for(s = 0; s < samp; s++)
            buffer_l[s] = SOX_SAMPLE_TO_FLOAT_32BIT(buf[s], clips);
    }
    else
    {
        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);
    if (p->mp3_buffer_size < new_buffer_size) {
      unsigned char *new_buffer = lsx_realloc(p->mp3_buffer, new_buffer_size);
      p->mp3_buffer_size = new_buffer_size;
      p->mp3_buffer = new_buffer;
    }

    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, (size_t)written) < (size_t)written)
    {
        lsx_fail_errno(ft,SOX_EOF,"file write failed");
        return 0;
    }

    return samp;
}

static int stopwrite(sox_format_t * ft)
{
  priv_t *p = (priv_t *) ft->priv;
#ifdef HAVE_LAME
  uint64_t num_samples = ft->olength == SOX_IGNORE_LENGTH ? 0 : ft->olength / max(ft->signal.channels, 1);
#endif
  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 (!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);

  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 || HAVE_TWOLAME) */

#define startwrite NULL
#define sox_mp3write NULL
#define stopwrite NULL

#endif /* HAVE_LAME || HAVE_TWOLAME */

/* MAD can tell the difference between Layer 1 and Layer 2 but decodes them both
 * with the same decoder (Layer 1 is a subset of Layer 2) but soxi might
 * tell people if they happen across an MP1 file */
LSX_FORMAT_HANDLER(mp1)
{
  static char const * const names[] = {"mp1", "m1a", NULL};
  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
    "MPEG-1 Layer 1 lossy audio compression", names, 0,
    startread, sox_mp3read, stopread,
    NULL, NULL, NULL,
    sox_mp3seek, NULL, NULL, sizeof(priv_t)
  };
  return &handler;
}

LSX_FORMAT_HANDLER(mp2)
{
  static char const * const names[] = {"mp2", NULL};
  static unsigned const write_encodings[] = {
    SOX_ENCODING_MP2, 0, 0};
  static sox_rate_t const write_rates[] = {
    16000, 22050, 24000, 32000, 44100, 48000, 0};
  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
    "MPEG-1 Layer 2 lossy audio compression", names, 0,
    startread, sox_mp3read, stopread,
    startwrite, sox_mp3write, stopwrite,
    sox_mp3seek, write_encodings, write_rates, sizeof(priv_t)
  };
  return &handler;
}

LSX_FORMAT_HANDLER(mp3)
{
  /* "mp3" is both the name of a dynamic format handler plugin and
   * a filename extension and to load the above two handlers --with-mp3=dyn
   * they must be listed here because init_format() relies on that to guess
   * the symbol names of lsx_mp[12]_format_fn() and "mp3", the one that
   * corresponds to the symbol name of this handler, must come first.
   */
  static char const * const names[] = {"mp3", "mp2", "mp1", 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-1 Layer 3 lossy audio compression", names, 0,
    startread, sox_mp3read, stopread,
    startwrite, sox_mp3write, stopwrite,
    sox_mp3seek, write_encodings, write_rates, sizeof(priv_t)
  };
  return &handler;
}
#endif /* defined(HAVE_MAD) || defined(HAVE_LAME) || defined(HAVE_TWOLAME) */
