/*
 *  mpadec - MPEG audio decoder
 *  Copyright (C) 2002-2004 Dmitriy Startsev (dstartsev@rambler.ru)
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* $Id: mpadec.c,v 1.2 2004/07/28 06:46:49 metal_man Exp $ */

#include "mpadec_internal.h"

extern const uint16_t crc_table[256];
extern void *synth_table[2][2][4][4];

const uint32_t bitmask[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F,
                               0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };

const int32_t frequency_table[9] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000 };

const int16_t bitrate_table[2][3][16] = {
  { { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 560 },
    { 0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 448 },
    { 0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384 } },
  { { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 320 },
    { 0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 192 },
    { 0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 192 } }
};

extern void init_tables(mpadec_t mpadec, FLOAT scale, int32_t sblimit);
extern void decode_layer1(mpadec_t mpadec, uint8_t *buffer);
extern void decode_layer2(mpadec_t mpadec, uint8_t *buffer);
extern void decode_layer3(mpadec_t mpadec, uint8_t *buffer);

uint32_t getbits(mpadec_t mpadec, unsigned n)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;

  while ((mpa->bits_left <= 24) && mpa->bytes_left) {
    mpa->bit_buffer = (mpa->bit_buffer << 8) | *mpa->next_byte++;
    mpa->bits_left += (uint8_t)8;
    mpa->bytes_left--;
  }
  while (mpa->bits_left <= 24) {
    mpa->bit_buffer = (mpa->bit_buffer << 8);
    mpa->bits_left += (uint8_t)8;
  }
  mpa->bits_left -= (uint8_t)n;
  return (mpa->bit_buffer >> mpa->bits_left) & bitmask[n];
}

uint16_t update_crc(uint16_t init, uint8_t *buf, int length)
{
  register uint32_t crc = (uint32_t)init, tmp;
  register int l = length;
  register uint8_t *b = buf;

  for (; l >= 8; l -= 8) crc = (crc << 8) ^ crc_table[((crc >> 8) ^ (*b++)) & 0xFF];
  tmp = (uint32_t)(*b) << 8;
  while (l--) {
    tmp <<= 1;
    crc <<= 1;
    if ((crc ^ tmp) & 0x10000) crc ^= 0x8005;
  }
  return (uint16_t)crc;
}

static uint32_t detect_frame_size(mpadec_t mpadec)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;
  register uint8_t *buf = mpa->next_byte;
  uint32_t i = mpa->bytes_left, hdr = 0;

  if (i < (mpa->frame.frame_size + 4)) return 0;
  buf += mpa->frame.frame_size;
  i -= mpa->frame.frame_size;
  while (i >= 4) {
    register uint32_t tmp = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
    if (((tmp & 0xFFE00000) == 0xFFE00000) && (tmp & (3 << 17)) && ((tmp & (3 << 10)) != (3 << 10))) {
      if ((mpa->frame.layer == (uint8_t)(4 - ((tmp >> 17) & 3))) &&
          (mpa->frame.frequency_index == (((tmp >> 10) & 3) + 3*(mpa->frame.LSF + mpa->frame.MPEG25))) &&
          (mpa->frame.channels == ((((tmp >> 6) & 3) == MPG_MD_MONO) ? 1 : 2))) {
        if (mpa->config.dblsync) {
          uint32_t fs = mpa->bytes_left - i - mpa->frame.padding + ((tmp >> 9) & 1);
          if (i >= (fs + 4)) {
            buf += fs;
            tmp = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
            buf -= fs;
            if (((tmp & 0xFFE00000) == 0xFFE00000) && (tmp & (3 << 17)) && ((tmp & (3 << 10)) != (3 << 10))) {
              if ((mpa->frame.layer == (uint8_t)(4 - ((tmp >> 17) & 3))) &&
                  (mpa->frame.frequency_index == (((tmp >> 10) & 3) + 3*(mpa->frame.LSF + mpa->frame.MPEG25))) &&
                  (mpa->frame.channels == ((((tmp >> 6) & 3) == MPG_MD_MONO) ? 1 : 2))) {
                hdr = tmp;
                break;
              }
            }
          } else return 0;
        } else {
          hdr = tmp;
          break;
        }
      }
    }
    buf++; i--;
  }
  return (hdr ? (mpa->bytes_left - i) : 0);
}

static int decode_header(mpadec_t mpadec, uint32_t header)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;
  unsigned int layer, bridx, fridx;

  layer = 4 - ((header >> 17) & 3);
  bridx = ((header >> 12) & 0x0F);
  fridx = ((header >> 10) & 3);
  if ((fridx == 3) || (layer == 4) || ((layer != 3) && !bridx)) return FALSE;
  if (header & (1 << 20)) {
    mpa->frame.LSF = (uint8_t)((header & (1 << 19)) ? FALSE : TRUE);
    mpa->frame.MPEG25 = FALSE;
  } else mpa->frame.LSF = mpa->frame.MPEG25 = TRUE;
  mpa->frame.layer = (uint8_t)layer;
  mpa->frame.bitrate_index = bridx;
  mpa->frame.bitrate = bitrate_table[mpa->frame.LSF][layer - 1][bridx];
  mpa->frame.frequency_index = (fridx += 3*(mpa->frame.LSF + mpa->frame.MPEG25));
  mpa->frame.frequency = frequency_table[fridx];
  mpa->frame.decoded_frequency = mpa->frame.frequency >> mpa->config.quality;
  mpa->frame.CRC = (uint8_t)(((header >> 16) & 1) ^ 1);
  mpa->frame.padding = ((header >> 9) & 1);
  mpa->frame.extension = (uint8_t)((header >> 8) & 1);
  mpa->frame.mode = (uint8_t)((header >> 6) & 3);
  mpa->frame.mode_ext = (uint8_t)((header >> 4) & 3);
  mpa->frame.copyright = (uint8_t)((header >> 3) & 1);
  mpa->frame.original = (uint8_t)((header >> 2) & 1);
  mpa->frame.emphasis = (uint8_t)(header & 3);
  mpa->frame.channels = (uint8_t)((mpa->frame.mode == MPG_MD_MONO) ? 1 : 2);
  switch (mpa->config.mode) {
    case MPADEC_CONFIG_CHANNEL1:
    case MPADEC_CONFIG_CHANNEL2:
    case MPADEC_CONFIG_MONO:     mpa->frame.decoded_channels = 1; break;
    case MPADEC_CONFIG_STEREO:   mpa->frame.decoded_channels = 2; break;
    default:                     mpa->frame.decoded_channels = mpa->frame.channels;
  }
  mpa->free_format = FALSE;
  switch (mpa->frame.layer) {
    case 1: mpa->frame.frame_samples = 384;
            mpa->frame.frame_size = (12000*mpa->frame.bitrate/mpa->frame.frequency + mpa->frame.padding) << 2;
            mpa->ssize = 0;
            break;
    case 2: mpa->frame.frame_samples = 1152;
            mpa->frame.frame_size = 144000*mpa->frame.bitrate/mpa->frame.frequency + mpa->frame.padding;
            mpa->ssize = 0;
            break;
    case 3: mpa->frame.frame_samples = 1152 >> mpa->frame.LSF;
            if (mpa->frame.LSF) mpa->ssize = (mpa->frame.channels > 1) ? 17 : 9;
            else mpa->ssize = (mpa->frame.channels > 1) ? 32 : 17;
            if (mpa->frame.bitrate_index) {
              mpa->frame.frame_size = 144000*mpa->frame.bitrate/(mpa->frame.frequency << mpa->frame.LSF) + mpa->frame.padding;
              mpa->prev_frame_size = 0;
            } else {
              register uint32_t div = 125*mpa->frame.frame_samples;
              if (!mpa->prev_frame_size) {
                mpa->frame.frame_size = (mpa->frame.CRC ? 6 : 4) + mpa->ssize + mpa->frame.padding;
                mpa->prev_frame_size = detect_frame_size(mpa);
                if (mpa->prev_frame_size) mpa->prev_frame_size -= mpa->frame.padding;
              }
              mpa->frame.frame_size = mpa->prev_frame_size + mpa->frame.padding;
              mpa->frame.bitrate = (mpa->frame.frame_size*mpa->frame.frequency + (div >> 1))/div;
              mpa->free_format = TRUE;
            }
            break;
  }
  mpa->frame.decoded_samples = mpa->frame.frame_samples >> mpa->config.quality;
  mpa->frame.downsample = FALSE;
  mpa->frame.downsample_sblimit = SBLIMIT;
  mpa->frame.decoded_size = mpa->frame.decoded_samples*mpa->frame.decoded_channels;
  switch (mpa->config.format) {
    case MPADEC_CONFIG_24BIT: mpa->frame.decoded_size *= 3; break;
    case MPADEC_CONFIG_32BIT:
    case MPADEC_CONFIG_FLOAT: mpa->frame.decoded_size <<= 2; break;
    default:                  mpa->frame.decoded_size <<= 1; break;
  }
  mpa->hsize = mpa->frame.CRC ? 6 : 4;
  if (mpa->frame.frame_size < (mpa->hsize + mpa->ssize)) mpa->frame.frame_size = mpa->hsize + mpa->ssize;
  mpa->dsize = mpa->frame.frame_size - (mpa->hsize + mpa->ssize);
  return TRUE;
}

static uint32_t sync_buffer(mpadec_t mpadec)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;
  register uint8_t *buf = mpa->next_byte;
  uint32_t retval = 0, i = mpa->bytes_left;

  if (mpa->state == MPADEC_STATE_START) {
    while (i >= 4) {
      register uint32_t tmp = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
      if (((tmp & 0xFFE00000) == 0xFFE00000) && (tmp & (3 << 17)) && ((tmp & (3 << 10)) != (3 << 10))) {
        if (mpa->config.dblsync) {
          if (decode_header(mpa, tmp)) {
            if ((i < (mpa->frame.frame_size + 4)) || (mpa->free_format && !mpa->prev_frame_size)) break;
            else {
              register uint32_t tmp2 = (buf[mpa->frame.frame_size] << 24) | (buf[mpa->frame.frame_size + 1] << 16) | (buf[mpa->frame.frame_size + 2] << 8) | buf[mpa->frame.frame_size + 3];
              if (((tmp2 & 0xFFE00000) == 0xFFE00000) && (tmp2 & (3 << 17)) && ((tmp2 & (3 << 10)) != (3 << 10))) {
                if ((mpa->frame.layer == (uint8_t)(4 - ((tmp2 >> 17) & 3))) &&
                    (mpa->frame.frequency_index == (((tmp2 >> 10) & 3) + 3*(mpa->frame.LSF + mpa->frame.MPEG25))) &&
                    (mpa->frame.channels == ((((tmp2 >> 6) & 3) == MPG_MD_MONO) ? 1 : 2))) {
                  retval = tmp;
                  break;
                }
              }
            }
          }
        } else {
          retval = tmp;
          break;
        }
      }
      buf++; i--;
    }
  } else {
    while (i >= 4) {
      register uint32_t tmp = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
      if (((tmp & 0xFFE00000) == 0xFFE00000) && (tmp & (3 << 17)) && ((tmp & (3 << 10)) != (3 << 10))) {
        if ((mpa->frame.layer == (uint8_t)(4 - ((tmp >> 17) & 3))) &&
            (mpa->frame.frequency_index == (((tmp >> 10) & 3) + 3*(mpa->frame.LSF + mpa->frame.MPEG25))) &&
            (mpa->frame.channels == ((((tmp >> 6) & 3) == MPG_MD_MONO) ? 1 : 2))) {
          retval = tmp;
          break;
        }
      }
      buf++; i--;
    }
  }
  if (i < mpa->bytes_left) {
    i = mpa->bytes_left - i;
    mpa->next_byte = buf;;
    mpa->bytes_left -= i;
    if (i >= 512) {
      mpa->reservoir_size = 0;
      i = 512;
    }
    memcpy(mpa->reservoir + mpa->reservoir_size, mpa->next_byte - i, i);
    mpa->reservoir_size += i;
  }
  return retval;
}

static int first_frame(mpadec_t mpadec)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;
  int i, skip = FALSE;
  uint32_t framesize; FLOAT scale;

  if (mpa->frame.channels > 1) i = ((mpa->config.mode == MPADEC_CONFIG_STEREO) || (mpa->config.mode == MPADEC_CONFIG_AUTO)) ? 3 : 2;
  else i = (mpa->config.mode == MPADEC_CONFIG_STEREO) ? 1 : 0;
  mpa->synth_func = synth_table[mpa->config.quality][mpa->config.endian][mpa->config.format][i];
  mpa->sample_size = mpa->frame.decoded_channels;
  switch (mpa->config.format) {
    case MPADEC_CONFIG_24BIT: mpa->sample_size *= 3; scale = 0x800000; break;
    case MPADEC_CONFIG_32BIT: mpa->sample_size <<= 2; scale = 0x80000000; break;
    case MPADEC_CONFIG_FLOAT: mpa->sample_size <<= 2; scale = 1; break;
    default:                  mpa->sample_size <<= 1; scale = 0x8000; break;
  }
  mpa->synth_size = (mpa->sample_size << 5) >> mpa->config.quality;
  if (mpa->config.replaygain != MPADEC_CONFIG_REPLAYGAIN_CUSTOM) {
    mpa->config.gain = 0.0;
    mpa->replay_gain = 1.0;
  }
  mpa->skip_samples = 0;
  mpa->padding_samples = 0;
  mpa->decoded_frames = 0;
  mpa->decoded_samples = 0;
  memset(&mpa->tag_info, 0, sizeof(mpa->tag_info));
  framesize = (mpa->frame.frame_size < mpa->bytes_left) ? mpa->frame.frame_size : mpa->bytes_left;
  if ((mpa->frame.layer == 3) && (framesize >= (mpa->ssize + 124))) {
    register uint8_t *buf = mpa->next_byte + 4 + mpa->ssize;
    if (((buf[0] == 'X') && (buf[1] == 'i') && (buf[2] == 'n') && (buf[3] == 'g')) ||
        ((buf[0] == 'I') && (buf[1] == 'n') && (buf[2] == 'f') && (buf[3] == 'o'))) {
      skip = TRUE;
      mpa->next_byte += framesize;
      mpa->bytes_left -= framesize;
      buf += 4;
      mpa->tag_info.flags = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
      buf += 4;
      if (mpa->tag_info.flags & 1) {
        mpa->tag_info.frames = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
        buf += 4;
      };
      if (mpa->tag_info.flags & 2) {
        mpa->tag_info.bytes = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
        buf += 4;
      };
      if (mpa->tag_info.flags & 4) {
        memcpy(mpa->tag_info.toc, buf, 100);
        buf += 100;
      };
      if (mpa->tag_info.flags & 8) buf += 4;
      mpa->tag_info.flags &= 7;
      if (framesize >= mpa->ssize + 160) {
        buf += 15;
        mpa->tag_info.replay_gain[0] = ((buf[0] << 8) | buf[1]) & 0x1FF;
        if (buf[0] & 2) mpa->tag_info.replay_gain[0] = -mpa->tag_info.replay_gain[0];
        buf += 2;
        mpa->tag_info.replay_gain[1] = ((buf[0] << 8) | buf[1]) & 0x1FF;
        if (buf[0] & 2) mpa->tag_info.replay_gain[1] = -mpa->tag_info.replay_gain[1];
        buf += 4;
        mpa->tag_info.enc_delay = (buf[0] << 4) | ((buf[1] >> 4) & 0x0F);
        mpa->tag_info.enc_padding = ((buf[1] & 0x0F) << 8) | buf[2];
        if (((mpa->tag_info.enc_delay < 0) || (mpa->tag_info.enc_delay > 3000)) ||
            ((mpa->tag_info.enc_padding < 0) || (mpa->tag_info.enc_padding > 3000))) {
          mpa->tag_info.replay_gain[0] = mpa->tag_info.replay_gain[1] = 0;
          mpa->tag_info.enc_delay = 0;
          mpa->tag_info.enc_padding = 0;
        } else {
          if (mpa->config.replaygain == MPADEC_CONFIG_REPLAYGAIN_RADIO) {
            mpa->config.gain = ((FLOAT)mpa->tag_info.replay_gain[0])/10.0;
          } else if (mpa->config.replaygain == MPADEC_CONFIG_REPLAYGAIN_AUDIOPHILE) {
            mpa->config.gain = ((FLOAT)mpa->tag_info.replay_gain[1])/10.0;
          }
        }
        mpa->skip_samples = mpa->tag_info.enc_delay;
        mpa->padding_samples = mpa->tag_info.enc_padding;
      }
    }
  }
  mpa->replay_gain = pow(10.0, mpa->config.gain/20.0);
  if (mpa->config.replaygain != MPADEC_CONFIG_REPLAYGAIN_NONE) scale *= mpa->replay_gain;
  init_tables(mpa, scale, SBLIMIT >> mpa->config.quality);
  if (mpa->frame.layer == 3) {
    mpa->skip_samples += 529;
    if ((mpa->tag_info.flags & 1) && (mpa->padding_samples > 529)) mpa->padding_samples -= 529;
    else mpa->padding_samples = 0;
  } else {
    mpa->skip_samples += 241;
    mpa->padding_samples = 0;
  }
  mpa->padding_start = mpa->tag_info.frames*mpa->frame.frame_samples;
  if (mpa->padding_start > mpa->padding_samples) mpa->padding_start -= mpa->padding_samples;
  else mpa->padding_start = 0;
  mpa->state = MPADEC_STATE_DECODE;
  return skip;
}

mpadec_t MPADECAPI mpadec_init(void)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)malloc(sizeof(struct mpadec_t));
  uint8_t temp[2];

  if (!mpa) return NULL;
  memset(mpa, 0, sizeof(struct mpadec_t));
  mpa->size = sizeof(struct mpadec_t);
  *((int16_t *)temp) = 1;
  mpa->config.quality = MPADEC_CONFIG_FULL_QUALITY;
  mpa->config.mode = MPADEC_CONFIG_AUTO;
  mpa->config.format = MPADEC_CONFIG_16BIT;
  mpa->config.endian = (uint8_t)((uint8_t)temp[0] ? MPADEC_CONFIG_LITTLE_ENDIAN : MPADEC_CONFIG_BIG_ENDIAN);
  mpa->config.replaygain = MPADEC_CONFIG_REPLAYGAIN_NONE;
  mpa->config.skip = TRUE;
  mpa->config.crc = TRUE;
  mpa->config.dblsync = TRUE;
  mpa->config.gain = 0.0;
  mpa->replay_gain = 1.0;
  init_tables(mpa, 0x8000, SBLIMIT);
  mpa->synth_bufoffs = 1;
  mpa->state = MPADEC_STATE_START;
  return mpa;
}

int MPADECAPI mpadec_uninit(mpadec_t mpadec)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;

  if (mpa && (mpa->size == sizeof(struct mpadec_t))) {
    mpa->size = mpa->state = 0;
    free(mpa);
    return MPADEC_RETCODE_OK;
  } else return MPADEC_RETCODE_INVALID_HANDLE;
}

int MPADECAPI mpadec_reset(mpadec_t mpadec)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;

  if (mpa && (mpa->size == sizeof(struct mpadec_t))) {
    memset(mpa->hybrid_in, 0, sizeof(mpa->hybrid_in));
    memset(mpa->hybrid_out, 0, sizeof(mpa->hybrid_out));
    memset(mpa->hybrid_buffers, 0, sizeof(mpa->hybrid_buffers));
    memset(mpa->synth_buffers, 0, sizeof(mpa->synth_buffers));
    mpa->hybrid_block[0] = mpa->hybrid_block[1] = 0;
    mpa->synth_bufoffs = 1;
    memset(&mpa->tag_info, 0, sizeof(mpa->tag_info));
    if (mpa->config.replaygain != MPADEC_CONFIG_REPLAYGAIN_CUSTOM) mpa->config.gain = 0.0;
    mpa->prev_frame_size = 0;
    mpa->free_format = FALSE;
    mpa->error = FALSE;
    mpa->reservoir_size = 0;
    mpa->replay_gain = 1.0;
    mpa->skip_samples = 0;
    mpa->padding_samples = 0;
    mpa->decoded_frames = 0;
    mpa->decoded_samples = 0;
    mpa->state = MPADEC_STATE_START;
    return MPADEC_RETCODE_OK;
  } else return MPADEC_RETCODE_INVALID_HANDLE;
}

int MPADECAPI mpadec_configure(mpadec_t mpadec, mpadec_config_t *cfg)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;
  int32_t i, sblimit; FLOAT scale;

  if (mpa && (mpa->size == sizeof(struct mpadec_t))) {
    if (!cfg) return MPADEC_RETCODE_INVALID_PARAMETERS;
    if ((cfg->quality > MPADEC_CONFIG_HALF_QUALITY) || (cfg->mode > MPADEC_CONFIG_CHANNEL2) ||
        (cfg->format > MPADEC_CONFIG_FLOAT) || (cfg->endian > MPADEC_CONFIG_BIG_ENDIAN) ||
        (cfg->replaygain > MPADEC_CONFIG_REPLAYGAIN_CUSTOM)) return MPADEC_RETCODE_INVALID_PARAMETERS;
    mpa->config.quality = cfg->quality;
    mpa->config.mode = cfg->mode;
    mpa->config.format = cfg->format;
    mpa->config.endian = cfg->endian;
    mpa->config.replaygain = cfg->replaygain;
    mpa->config.skip = (uint8_t)(cfg->skip ? TRUE : FALSE);
    mpa->config.crc = (uint8_t)(cfg->crc ? TRUE : FALSE);
    mpa->config.dblsync = (uint8_t)(cfg->dblsync ? TRUE : FALSE);
    if (mpa->config.replaygain == MPADEC_CONFIG_REPLAYGAIN_CUSTOM) {
      mpa->config.gain = cfg->gain;
    } else {
      mpa->config.gain = 0.0;
      if (mpa->tag_info.flags) {
        if (mpa->config.replaygain == MPADEC_CONFIG_REPLAYGAIN_RADIO) {
          mpa->config.gain = ((FLOAT)mpa->tag_info.replay_gain[0])/10.0;
        } else if (mpa->config.replaygain == MPADEC_CONFIG_REPLAYGAIN_AUDIOPHILE) {
          mpa->config.gain = ((FLOAT)mpa->tag_info.replay_gain[1])/10.0;
        }
      }
    }
    mpa->replay_gain = pow(10.0, mpa->config.gain/20.0);
    switch (mpa->config.format) {
      case MPADEC_CONFIG_24BIT: scale = 0x800000; break;
      case MPADEC_CONFIG_32BIT: scale = 0x80000000; break;
      case MPADEC_CONFIG_FLOAT: scale = 1; break;
      default:                  scale = 0x8000; break;
    }
    sblimit = SBLIMIT >> mpa->config.quality;
    if (mpa->config.replaygain != MPADEC_CONFIG_REPLAYGAIN_NONE) scale *= mpa->replay_gain;
    init_tables(mpa, scale, sblimit);
    if ((mpa->state > MPADEC_STATE_START) && mpa->header) {
      decode_header(mpa, mpa->header);
      if (mpa->frame.channels < 2) i = (mpa->config.mode == MPADEC_CONFIG_STEREO) ? 1 : 0;
      else i = ((mpa->config.mode == MPADEC_CONFIG_STEREO) || (mpa->config.mode == MPADEC_CONFIG_AUTO)) ? 3 : 2;
      mpa->synth_func = synth_table[mpa->config.quality][mpa->config.endian][mpa->config.format][i];
      mpa->sample_size = mpa->frame.decoded_channels;
      switch (mpa->config.format) {
        case MPADEC_CONFIG_24BIT: mpa->sample_size *= 3; break;
        case MPADEC_CONFIG_32BIT:
        case MPADEC_CONFIG_FLOAT: mpa->sample_size <<= 2; break;
        default:                  mpa->sample_size <<= 1; break;
      }
      mpa->synth_size = (mpa->sample_size << 5) >> mpa->config.quality;
    } else mpa->state = MPADEC_STATE_START;
    return MPADEC_RETCODE_OK;
  } else return MPADEC_RETCODE_INVALID_HANDLE;
}

int MPADECAPI mpadec_get_info(mpadec_t mpadec, void *info, int info_type)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;

  if (!mpa || (mpa->size != sizeof(struct mpadec_t))) return MPADEC_RETCODE_INVALID_HANDLE;
  if (!info) return MPADEC_RETCODE_INVALID_PARAMETERS;
  if (info_type == MPADEC_INFO_CONFIG) {
    register mpadec_config_t *cfg = (mpadec_config_t *)info;
    cfg->quality = mpa->config.quality;
    cfg->mode = mpa->config.mode;
    cfg->format = mpa->config.format;
    cfg->endian = mpa->config.endian;
    cfg->replaygain = mpa->config.replaygain;
    cfg->skip = mpa->config.skip;
    cfg->crc = mpa->config.crc;
    cfg->dblsync = mpa->config.dblsync;
    cfg->gain = mpa->config.gain;
  } else if (info_type == MPADEC_INFO_TAG) {
    register mp3tag_info_t *tag = (mp3tag_info_t *)info;
    if (mpa->state < MPADEC_STATE_DECODE) {
      memset(tag, 0, sizeof(mp3tag_info_t));
      return MPADEC_RETCODE_BAD_STATE;
    } else memcpy(tag, &mpa->tag_info, sizeof(mpa->tag_info));
  } else if (info_type == MPADEC_INFO_STREAM) {
    register mpadec_info_t *inf = (mpadec_info_t *)info;
    if (mpa->state < MPADEC_STATE_DECODE) {
      memset(inf, 0, sizeof(mpadec_info_t));
      return MPADEC_RETCODE_BAD_STATE;
    } else {
      inf->layer = mpa->frame.layer;
      inf->channels = mpa->frame.channels;
      inf->frequency = mpa->frame.frequency;
      inf->bitrate = mpa->frame.bitrate;
      inf->mode = mpa->frame.mode;
      inf->copyright = mpa->frame.copyright;
      inf->original = mpa->frame.original;
      inf->emphasis = mpa->frame.emphasis;
      inf->decoded_channels = mpa->frame.decoded_channels;
      inf->decoded_frequency = mpa->frame.decoded_frequency;
      inf->decoded_sample_size = mpa->sample_size;
      inf->frame_size = mpa->frame.frame_size;
      inf->frame_samples = mpa->frame.frame_samples;
      inf->decoded_frame_samples = mpa->frame.decoded_samples;
      if (mpa->tag_info.flags & 1) {
        inf->frames = mpa->tag_info.frames;
        inf->duration = (mpa->tag_info.frames*mpa->frame.frame_samples + (mpa->frame.frequency >> 1))/mpa->frame.frequency;
      } else {
        inf->frames = 0;
        inf->duration = 0;
      }
    }
  } else return MPADEC_RETCODE_INVALID_PARAMETERS;
  return MPADEC_RETCODE_OK;
}

int MPADECAPI mpadec_decode(mpadec_t mpadec, uint8_t *srcbuf, uint32_t srcsize, uint8_t *dstbuf, uint32_t dstsize, uint32_t *srcused, uint32_t *dstused)
{
  register struct mpadec_t *mpa = (struct mpadec_t *)mpadec;
  int retcode = MPADEC_RETCODE_OK;
  uint32_t decoded_size = 0;

  if (srcused) *srcused = 0;
  if (dstused) *dstused = 0;
  if (!dstbuf) dstsize = 0;
  if (!mpa || (mpa->size != sizeof(struct mpadec_t))) return MPADEC_RETCODE_INVALID_HANDLE;
  if (mpa->state < MPADEC_STATE_START) return MPADEC_RETCODE_BAD_STATE;
  if (!srcbuf || ((mpa->state > MPADEC_STATE_START) && !dstbuf )) return MPADEC_RETCODE_INVALID_PARAMETERS;
  mpa->next_byte = srcbuf;
  mpa->bytes_left = srcsize;
  while (mpa->bytes_left >= 4) {
    mpa->error = FALSE;
    mpa->bits_left = 0;
    mpa->header = sync_buffer(mpa);
    if (!mpa->header) {
      if (!decoded_size) retcode = MPADEC_RETCODE_NO_SYNC;
      break;
    }
    decode_header(mpa, mpa->header);
    if ((mpa->bytes_left < mpa->frame.frame_size) || (mpa->free_format && !mpa->prev_frame_size)) {
      retcode =  MPADEC_RETCODE_NEED_MORE_DATA;
      break;
    }
    if (mpa->state == MPADEC_STATE_START) {
      if (first_frame(mpa)) continue;
    } else if ((mpa->frame.layer == 3) && (mpa->frame.frame_size >= (mpa->ssize + 12))) {
      register uint8_t *buf = mpa->next_byte + 4 + mpa->ssize;
      if (((buf[0] == 'X') && (buf[1] == 'i') && (buf[2] == 'n') && (buf[3] == 'g')) ||
          ((buf[0] == 'I') && (buf[1] == 'n') && (buf[2] == 'f') && (buf[3] == 'o'))) {
        mpa->next_byte += mpa->frame.frame_size;
        mpa->bytes_left -= mpa->frame.frame_size;
        continue;
      }
    }
    if (dstsize < mpa->frame.decoded_size) {
      retcode = MPADEC_RETCODE_BUFFER_TOO_SMALL;
      break;
    }
    if (mpa->config.crc && mpa->frame.CRC) {
      mpa->frame.crc = (uint16_t)((mpa->next_byte[4] << 8) | mpa->next_byte[5]);
      mpa->crc = update_crc(0xFFFF, mpa->next_byte + 2, 16);
    }
    mpa->next_byte += mpa->hsize;
    mpa->bytes_left -= mpa->hsize;
    switch (mpa->frame.layer) {
      case 1: decode_layer1(mpa, dstbuf); break;
      case 2: decode_layer2(mpa, dstbuf); break;
      case 3: decode_layer3(mpa, dstbuf); break;
    }
    if (mpa->config.crc && mpa->error) memset(dstbuf, 0, mpa->frame.decoded_size);
    dstbuf += mpa->frame.decoded_size;
    dstsize -= mpa->frame.decoded_size;
    decoded_size += mpa->frame.decoded_size;
    mpa->decoded_samples += mpa->frame.frame_samples;
    mpa->decoded_frames++;
    if (mpa->config.skip) {
      if (mpa->skip_samples) {
        if (mpa->skip_samples >= mpa->frame.frame_samples) {
          dstbuf -= mpa->frame.decoded_size;
          dstsize += mpa->frame.decoded_size;
          decoded_size -= mpa->frame.decoded_size;
          mpa->skip_samples -= mpa->frame.frame_samples;
        } else {
          uint32_t tmp = mpa->frame.frame_samples - mpa->skip_samples;
          tmp = mpa->sample_size*(tmp >> mpa->config.quality);
          memmove(dstbuf - mpa->frame.decoded_size, dstbuf - tmp, tmp);
          dstbuf -= mpa->frame.decoded_size - tmp;
          dstsize += mpa->frame.decoded_size - tmp;
          decoded_size -= mpa->frame.decoded_size - tmp;
          mpa->skip_samples = 0;
        }
      } else if ((mpa->padding_samples) && (mpa->decoded_samples > mpa->padding_start)) {
        uint32_t tmp = mpa->decoded_samples - mpa->padding_start;
        if (tmp > mpa->padding_samples) tmp = mpa->padding_samples;
        mpa->padding_start += tmp;
        mpa->padding_samples -= tmp;
        tmp = mpa->sample_size*(tmp >> mpa->config.quality);
        dstbuf -= tmp;
        dstsize += tmp;
        decoded_size -= tmp;
      }
    }
  }
  if (srcused) *srcused = srcsize - mpa->bytes_left;
  if (dstused) *dstused = decoded_size;
  if ((retcode == MPADEC_RETCODE_OK) && mpa->bytes_left) retcode = MPADEC_RETCODE_NEED_MORE_DATA;
  if (!dstbuf && (retcode == MPADEC_RETCODE_BUFFER_TOO_SMALL)) retcode = MPADEC_RETCODE_OK;
  return retcode;
}

char * MPADECAPI mpadec_error(int code)
{
  static char *mpa_errors[] = { "No error",
                                "Invalid handle",
                                "Not enough memory",
                                "Bad decoder state",
                                "Invalid parameters",
                                "Need more data",
                                "Output buffer too small",
                                "Unable to synchronize",
                                "Unknown error" };
  if (code > MPADEC_RETCODE_UNKNOWN) code = MPADEC_RETCODE_UNKNOWN;
  return mpa_errors[code];
}

mpadec2_t MPADECAPI mpadec2_init(void)
{
  register struct mpadec2_t *mpa = (struct mpadec2_t *)malloc(sizeof(struct mpadec2_t));

  if (!mpa) return NULL;
  mpa->size = sizeof(struct mpadec2_t);
  mpa->buffers = NULL;
  mpa->in_buffer_offset = mpa->in_buffer_used = 0;
  mpa->out_buffer_offset = mpa->out_buffer_used = 0;
  mpa->mpadec = mpadec_init();
  if (!mpa->mpadec) {
    free(mpa);
    return NULL;
  }
  return mpa;
}

int MPADECAPI mpadec2_uninit(mpadec2_t mpadec)
{
  register struct mpadec2_t *mpa = (struct mpadec2_t *)mpadec;

  if (mpa && (mpa->size == sizeof(struct mpadec2_t))) {
    struct mpabuffer_t *buf = mpa->buffers, *tmp;
    while (buf) {
      tmp = buf;
      buf = buf->next;
      free(tmp);
    }
    mpadec_uninit(mpa->mpadec);
    free(mpa);
    return MPADEC_RETCODE_OK;
  } else return MPADEC_RETCODE_INVALID_HANDLE;
}

int MPADECAPI mpadec2_reset(mpadec2_t mpadec)
{
  register struct mpadec2_t *mpa = (struct mpadec2_t *)mpadec;

  if (mpa && (mpa->size == sizeof(struct mpadec2_t))) {
    struct mpabuffer_t *buf = mpa->buffers, *tmp;
    while (buf) {
      tmp = buf;
      buf = buf->next;
      free(tmp);
    }
    mpa->buffers = NULL;
    mpa->in_buffer_offset = mpa->in_buffer_used = 0;
    mpa->out_buffer_offset = mpa->out_buffer_used = 0;
    mpadec_reset(mpa->mpadec);
    return MPADEC_RETCODE_OK;
  } else return MPADEC_RETCODE_INVALID_HANDLE;
}

int MPADECAPI mpadec2_configure(mpadec2_t mpadec, mpadec_config_t *cfg)
{
  register struct mpadec2_t *mpa = (struct mpadec2_t *)mpadec;

  if (!mpa || (mpa->size != sizeof(struct mpadec2_t))) return MPADEC_RETCODE_INVALID_HANDLE;
  mpa->out_buffer_offset = mpa->out_buffer_used = 0;
  return (mpadec_configure(mpa->mpadec, cfg));
}

int MPADECAPI mpadec2_get_info(mpadec2_t mpadec, void *info, int info_type)
{
  register struct mpadec2_t *mpa = (struct mpadec2_t *)mpadec;

  if (!mpa || (mpa->size != sizeof(struct mpadec2_t))) return MPADEC_RETCODE_INVALID_HANDLE;
  return (mpadec_get_info(mpa->mpadec, info, info_type));
}

int MPADECAPI mpadec2_decode(mpadec2_t mpadec, uint8_t *srcbuf, uint32_t srcsize, uint8_t *dstbuf, uint32_t dstsize, uint32_t *dstused)
{
  register struct mpadec2_t *mpa = (struct mpadec2_t *)mpadec;
  uint32_t n, src_used, dst_used; int r;

  if (dstused) *dstused = 0;
  if (!mpa || (mpa->size != sizeof(struct mpadec2_t))) return MPADEC_RETCODE_INVALID_HANDLE;
  if (((struct mpadec_t *)mpa->mpadec)->state < MPADEC_STATE_START) return MPADEC_RETCODE_BAD_STATE;
  if (srcbuf && srcsize) {
    struct mpabuffer_t *last = mpa->buffers, *buf;
    if (last) {
      while (last->next) last = last->next;
      if ((last->offset + last->used) < last->size) {
        n = last->size - (last->offset + last->used);
        if (n > srcsize) n = srcsize;
        memcpy(last->buffer + last->offset + last->used, srcbuf, n);
        last->used += n;
        srcbuf += n;
        srcsize -= n;
      }
    }
    if (srcsize) {
      n = (srcsize > 4096) ? srcsize : 4096;
      buf = (struct mpabuffer_t *)malloc(n + sizeof(struct mpabuffer_t));
      if (buf) {
        buf->size = n;
        buf->offset = buf->used = 0;
        buf->buffer = (uint8_t *)buf + sizeof(struct mpabuffer_t);
        buf->next = NULL;
        memcpy(buf->buffer, srcbuf, srcsize);
        buf->used = srcsize;
        if (last) last->next = buf; else mpa->buffers = buf;
      } else return MPADEC_RETCODE_NOT_ENOUGH_MEMORY;
    }
  }
  if (!dstbuf || !dstsize) return MPADEC_RETCODE_OK;
  while (dstsize) {
    struct mpabuffer_t *buf = mpa->buffers;
    if (mpa->out_buffer_used) {
      n = (dstsize < mpa->out_buffer_used) ? dstsize : mpa->out_buffer_used;
      memcpy(dstbuf, mpa->out_buffer + mpa->out_buffer_offset, n);
      mpa->out_buffer_offset += n;
      mpa->out_buffer_used -= n;
      dstbuf += n;
      dstsize -= n;
      if (dstused) *dstused += n;
    }
    if (!dstsize) break;
    if (mpa->in_buffer_used && mpa->in_buffer_offset) memmove(mpa->in_buffer, mpa->in_buffer + mpa->in_buffer_offset, mpa->in_buffer_used);
    mpa->in_buffer_offset = 0;
    while (buf && (mpa->in_buffer_used < sizeof(mpa->in_buffer))) {
      if (buf->used) {
        n = sizeof(mpa->in_buffer) - mpa->in_buffer_used;
        if (n > buf->used) n = buf->used;
        memcpy(mpa->in_buffer + mpa->in_buffer_offset + mpa->in_buffer_used, buf->buffer + buf->offset, n);
        buf->offset += n;
        buf->used -= n;
        mpa->in_buffer_used += n;
      }
      if (!buf->used) {
        struct mpabuffer_t *tmp = buf;
        buf = buf->next;
        free(tmp);
      }
    }
    mpa->buffers = buf;
    r = mpadec_decode(mpa->mpadec, mpa->in_buffer + mpa->in_buffer_offset, mpa->in_buffer_used, dstbuf, dstsize, &src_used, &dst_used);
    mpa->in_buffer_offset += src_used;
    mpa->in_buffer_used -= src_used;
    dstbuf += dst_used;
    dstsize -= dst_used;
    if (dstused) *dstused += dst_used;
    if (r == MPADEC_RETCODE_BUFFER_TOO_SMALL) {
      mpa->out_buffer_offset = mpa->out_buffer_used = 0;
      mpadec_decode(mpa->mpadec, mpa->in_buffer + mpa->in_buffer_offset, mpa->in_buffer_used, mpa->out_buffer, sizeof(mpa->out_buffer), &src_used, &mpa->out_buffer_used);
      mpa->in_buffer_offset += src_used;
      mpa->in_buffer_used -= src_used;
      if (!mpa->out_buffer_used) break;
    } else if (!mpa->buffers) break;
  }
  return MPADEC_RETCODE_OK;
}

