/*
 * Siren Encoder/Decoder library
 *
 *   @author: Youness Alaoui <kakaroto@kakaroto.homelinux.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */


#include "siren7.h"

SirenDecoder
Siren7_NewDecoder (int sample_rate)
{
  SirenDecoder decoder = (SirenDecoder) malloc (sizeof (struct stSirenDecoder));
  decoder->sample_rate = sample_rate;

  decoder->WavHeader.riff.RiffId = ME_TO_LE32 (RIFF_ID);
  decoder->WavHeader.riff.RiffSize = sizeof (PCMWavHeader) - 2 * sizeof (int);
  decoder->WavHeader.riff.RiffSize =
      ME_TO_LE32 (decoder->WavHeader.riff.RiffSize);
  decoder->WavHeader.WaveId = ME_TO_LE32 (WAVE_ID);

  decoder->WavHeader.FmtId = ME_TO_LE32 (FMT__ID);
  decoder->WavHeader.FmtSize = ME_TO_LE32 (sizeof (FmtChunk));

  decoder->WavHeader.fmt.Format = ME_TO_LE16 (0x01);
  decoder->WavHeader.fmt.Channels = ME_TO_LE16 (1);
  decoder->WavHeader.fmt.SampleRate = ME_TO_LE32 (16000);
  decoder->WavHeader.fmt.ByteRate = ME_TO_LE32 (32000);
  decoder->WavHeader.fmt.BlockAlign = ME_TO_LE16 (2);
  decoder->WavHeader.fmt.BitsPerSample = ME_TO_LE16 (16);

  decoder->WavHeader.FactId = ME_TO_LE32 (FACT_ID);
  decoder->WavHeader.FactSize = ME_TO_LE32 (sizeof (int));
  decoder->WavHeader.Samples = ME_TO_LE32 (0);

  decoder->WavHeader.DataId = ME_TO_LE32 (DATA_ID);
  decoder->WavHeader.DataSize = ME_TO_LE32 (0);

  memset (decoder->context, 0, sizeof (decoder->context));
  memset (decoder->backup_frame, 0, sizeof (decoder->backup_frame));

  decoder->dw1 = 1;
  decoder->dw2 = 1;
  decoder->dw3 = 1;
  decoder->dw4 = 1;

  siren_init ();
  return decoder;
}

void
Siren7_CloseDecoder (SirenDecoder decoder)
{
  free (decoder);
}

int
Siren7_DecodeFrame (SirenDecoder decoder, unsigned char *DataIn,
    unsigned char *DataOut)
{
  int number_of_coefs,
      sample_rate_bits,
      rate_control_bits,
      rate_control_possibilities,
      checksum_bits,
      esf_adjustment,
      scale_factor, number_of_regions, sample_rate_code, bits_per_frame;
  int decoded_sample_rate_code;

  static int absolute_region_power_index[28] = { 0 };
  static float decoder_standard_deviation[28] = { 0 };
  static int power_categories[28] = { 0 };
  static int category_balance[28] = { 0 };
  int ChecksumTable[4] = { 0x7F80, 0x7878, 0x6666, 0x5555 };
  int i, j;

  int dwRes = 0;
  int envelope_bits = 0;
  int rate_control = 0;
  int number_of_available_bits;
  int number_of_valid_coefs;
  int frame_error = 0;

  int In[20];
  float coefs[320];
  float BufferOut[320];
  int sum;
  int checksum;
  int calculated_checksum;
  int idx;
  int temp1;
  int temp2;

  for (i = 0; i < 20; i++)
#ifdef __BIG_ENDIAN__
    In[i] = ((short *) DataIn)[i];
#else
    In[i] =
        ((((short *) DataIn)[i] << 8) & 0xFF00) | ((((short *) DataIn)[i] >> 8)
        & 0x00FF);
#endif

  dwRes =
      GetSirenCodecInfo (1, decoder->sample_rate, &number_of_coefs,
      &sample_rate_bits, &rate_control_bits, &rate_control_possibilities,
      &checksum_bits, &esf_adjustment, &scale_factor, &number_of_regions,
      &sample_rate_code, &bits_per_frame);

  if (dwRes != 0)
    return dwRes;


  set_bitstream (In);

  decoded_sample_rate_code = 0;
  for (i = 0; i < sample_rate_bits; i++) {
    decoded_sample_rate_code <<= 1;
    decoded_sample_rate_code |= next_bit ();
  }


  if (decoded_sample_rate_code != sample_rate_code)
    return 7;

  number_of_valid_coefs = region_size * number_of_regions;
  number_of_available_bits = bits_per_frame - sample_rate_bits - checksum_bits;


  envelope_bits =
      decode_envelope (number_of_regions, decoder_standard_deviation,
      absolute_region_power_index, esf_adjustment);

  number_of_available_bits -= envelope_bits;

  for (i = 0; i < rate_control_bits; i++) {
    rate_control <<= 1;
    rate_control |= next_bit ();
  }

  number_of_available_bits -= rate_control_bits;

  categorize_regions (number_of_regions, number_of_available_bits,
      absolute_region_power_index, power_categories, category_balance);

  for (i = 0; i < rate_control; i++) {
    power_categories[category_balance[i]]++;
  }

  number_of_available_bits =
      decode_vector (decoder, number_of_regions, number_of_available_bits,
      decoder_standard_deviation, power_categories, coefs, scale_factor);


  frame_error = 0;
  if (number_of_available_bits > 0) {
    for (i = 0; i < number_of_available_bits; i++) {
      if (next_bit () == 0)
        frame_error = 1;
    }
  } else if (number_of_available_bits < 0
      && rate_control + 1 < rate_control_possibilities) {
    frame_error |= 2;
  }

  for (i = 0; i < number_of_regions; i++) {
    if (absolute_region_power_index[i] > 33
        || absolute_region_power_index[i] < -31)
      frame_error |= 4;
  }

  if (checksum_bits > 0) {
    bits_per_frame >>= 4;
    checksum = In[bits_per_frame - 1] & ((1 << checksum_bits) - 1);
    In[bits_per_frame - 1] &= ~checksum;
    sum = 0;
    idx = 0;
    do {
      sum ^= (In[idx] & 0xFFFF) << (idx % 15);
    } while (++idx < bits_per_frame);

    sum = (sum >> 15) ^ (sum & 0x7FFF);
    calculated_checksum = 0;
    for (i = 0; i < 4; i++) {
      temp1 = ChecksumTable[i] & sum;
      for (j = 8; j > 0; j >>= 1) {
        temp2 = temp1 >> j;
        temp1 ^= temp2;
      }
      calculated_checksum <<= 1;
      calculated_checksum |= temp1 & 1;
    }

    if (checksum != calculated_checksum)
      frame_error |= 8;
  }

  if (frame_error != 0) {
    for (i = 0; i < number_of_valid_coefs; i++) {
      coefs[i] = decoder->backup_frame[i];
      decoder->backup_frame[i] = 0;
    }
  } else {
    for (i = 0; i < number_of_valid_coefs; i++)
      decoder->backup_frame[i] = coefs[i];
  }


  for (i = number_of_valid_coefs; i < number_of_coefs; i++)
    coefs[i] = 0;


  dwRes = siren_rmlt_decode_samples (coefs, decoder->context, 320, BufferOut);


  for (i = 0; i < 320; i++) {
    if (BufferOut[i] > 32767.0)
      ((short *) DataOut)[i] = (short) ME_TO_LE16 ((short) 32767);
    else if (BufferOut[i] <= -32768.0)
      ((short *) DataOut)[i] = (short) ME_TO_LE16 ((short) 32768);
    else
      ((short *) DataOut)[i] = (short) ME_TO_LE16 ((short) BufferOut[i]);
  }

  decoder->WavHeader.Samples = ME_FROM_LE32 (decoder->WavHeader.Samples);
  decoder->WavHeader.Samples += 320;
  decoder->WavHeader.Samples = ME_TO_LE32 (decoder->WavHeader.Samples);
  decoder->WavHeader.DataSize = ME_FROM_LE32 (decoder->WavHeader.DataSize);
  decoder->WavHeader.DataSize += 640;
  decoder->WavHeader.DataSize = ME_TO_LE32 (decoder->WavHeader.DataSize);
  decoder->WavHeader.riff.RiffSize =
      ME_FROM_LE32 (decoder->WavHeader.riff.RiffSize);
  decoder->WavHeader.riff.RiffSize += 640;
  decoder->WavHeader.riff.RiffSize =
      ME_TO_LE32 (decoder->WavHeader.riff.RiffSize);


  return 0;
}