/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            liblame_wrapper.cc
 *
 *  Sat Jul  2 11:11:34 CEST 2005
 *  Copyright  2005 Bent Bisballe
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  This file is part of MIaV.
 *
 *  MIaV is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  MIaV 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with MIaV; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include <config.h>
#include "liblame_wrapper.h"
#include "miav_config.h"

LibLAMEWrapper::LibLAMEWrapper(Info *i)
{
  info = i;

  // Init library.
  if( (gfp = lame_init()) == NULL) {
    info->error("LAME initialization failed (due to malloc failure!)");
    return;
  }

	lame_set_in_samplerate(gfp, INPUT_SAMPLE_RATE);
	lame_set_out_samplerate(gfp, OUTPUT_SAMPLE_RATE);

 	lame_set_num_channels(gfp, CHANNELS);
  //  lame_set_num_samples(gfp, 1152);
  //  lame_set_num_samples(gfp, SAMPLES);
  //  lame_set_num_samples(gfp, 0);

	lame_set_quality(gfp, config->readInt("mp3_quality"));
	lame_set_mode(gfp, STEREO);
	lame_set_brate(gfp, config->readInt("mp3_bitrate"));

  lame_set_strict_ISO(gfp, 1);

  // 1 = write a Xing VBR header frame.
  lame_set_bWriteVbrTag(gfp, 0);

  // Types of VBR.  default = vbr_off = CBR
  //  lame_set_VBR(gfp, vbr_rh);

  // VBR quality level.  0=highest  9=lowest
  //  lame_set_VBR_q(gfp, 6);
  
  lame_set_copyright(gfp, 0);       // is there a copyright on the encoded data?
  lame_set_original(gfp, 1);        // is the encoded data on the original media?
  lame_set_error_protection(gfp, 0);// add 2 byte CRC protection to each frame?
  lame_set_padding_type(gfp, PAD_NO); // PAD_NO, PAD_ALL, PAD_ADJUST, PAD_MAX_INDICATOR 
                                    // 0 = do not pad frames
                                    // 1 = always pad frames
                                    // 2 = adjust padding to satisfy bit rate
  lame_set_extension(gfp, 0);       // private extension bit


	if (lame_init_params(gfp) < 0) {
    info->error("LAME parameter initialization failed.");
    return;
  }

  audio_buffer[0] = new int16_t[AUDIO_BUFFER_SIZE];
  audio_buffer[1] = new int16_t[AUDIO_BUFFER_SIZE];

  // And now for the dv decoder!
  decoder = NULL;

  calc_bitrate = 0;
  frame_number = 0;
}

LibLAMEWrapper::~LibLAMEWrapper()
{
  delete audio_buffer[0];
  delete audio_buffer[1];
}

Frame *LibLAMEWrapper::close(Frame *oldframe)
{
  Frame *frame;
  unsigned int offset = 0;

  frame = new Frame(NULL, (int)(1.25 * SAMPLES + 7200) * 2); // Big enough to hold two frames

  if(oldframe) {
    offset = oldframe->size;
    frame->number = oldframe->number;
    memcpy(frame->data, oldframe->data, oldframe->size);
    delete oldframe;
  }

  int flush;

  flush = lame_encode_finish(gfp, frame->data + offset, 7200);
     
  frame->size = offset + flush;

  calc_bitrate += flush;
  frame->bitrate = (unsigned int)((double)calc_bitrate / (double)(frame_number)) * 25;

  return frame;
}

#include <math.h>
static unsigned int sin_cnt = 0;
Frame *LibLAMEWrapper::encode(Frame *dvframe)
{
  if(dvframe->mute) {
    // Overwrite audiobuffer with dummy data
    double volume = 1000; // Min:= 0 - Max := 32000
    double frequency = 440; // in Hz

    for(int cnt = 0; cnt < SAMPLES; cnt++) {
      sin_cnt++;
      double sin_val = (((double)sin_cnt / (double)OUTPUT_SAMPLE_RATE) * (double)M_PI) * frequency;
      audio_buffer[0][cnt] = audio_buffer[1][cnt] = (short int)(sin(sin_val) * volume);
    }

    //    memset(audio_buffer[0], 0, sizeof(audio_buffer[0]));
    //    memset(audio_buffer[1], 0, sizeof(audio_buffer[1]));
  } else {
    // Decode audio from dv frame
    if(!decoder) {
      decoder = dv_decoder_new(FALSE/*this value is unused*/, FALSE, FALSE);
      decoder->quality = DV_QUALITY_BEST;
      
      dv_parse_header(decoder, dvframe->data);
      
      decoder->system = e_dv_system_625_50;  // PAL lines, PAL framerate
      decoder->sampling = e_dv_sample_422;  // 4 bytes y, 2 bytes u, 2 bytes v
      decoder->std = e_dv_std_iec_61834;
      decoder->num_dif_seqs = 12;
    }
    // Decode audio using libdv
    dv_decode_full_audio( decoder, dvframe->data, audio_buffer );
  }

  /**
   * input pcm data, output (maybe) mp3 frames.
   * This routine handles all buffering, resampling and filtering for you.
   * 
   * The required mp3buf_size can be computed from num_samples, 
   * samplerate and encoding rate, but here is a worst case estimate:
   *
   * return code     number of bytes output in mp3buffer.  can be 0 
   *                 if return code = -1:  mp3buffer was too small
   *
   * mp3buf_size in bytes = 1.25*num_samples + 7200
   *
   * I think a tighter bound could be:  (mt, March 2000)
   * MPEG1:
   *    num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512
   * MPEG2:
   *    num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256
   *
   * but test first if you use that!
   *
   * set mp3buf_size = 0 and LAME will not check if mp3buf_size is
   * large enough.
   *
   * NOTE:
   * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels
   * will be averaged into the L channel before encoding only the L channel
   * This will overwrite the data in buffer_l[] and buffer_r[].
   * 
   */
  Frame* audio_frame = new Frame(NULL, (int)(1.25 * SAMPLES + 7200));

  const short int    *buffer_l = audio_buffer[0];   // PCM data for left channel
  const short int    *buffer_r = audio_buffer[1];   // PCM data for right channel
  const int           nsamples = SAMPLES;      // number of samples per channel
  unsigned char*      mp3buf = audio_frame->data;        // pointer to encoded MP3 stream
  const int           mp3buf_size = audio_frame->size;   // number of valid octets in this

  int val;
  val = lame_encode_buffer(gfp, buffer_l, buffer_r, nsamples, mp3buf, mp3buf_size);
  // val = lame_encode_mp3_frame(gfp, buffer_l, buffer_r, mp3buf, mp3buf_size);
  
  //  info->info("Framenr: %d", lame_get_frameNum(gfp));

  if(val < 0) {
    switch(val) {
    case -1:  // mp3buf was too small
      info->error("Lame encoding failed, mp3buf was too small.");
      break;
    case -2:  // malloc() problem
      info->error("Lame encoding failed, due to malloc() problem.");
      break;
    case -3:  // lame_init_params() not called
      info->error("Lame encoding failed, lame_init_params() not called.");
      break;
    case -4:  // psycho acoustic problems 
      info->error("Lame encoding failed, due to psycho acoustic problems.");
      break;
    default:
      info->error("Lame encoding failed, due to unknown error.");
      break;
    }
  }

  /**
   * OPTIONAL:
   * lame_encode_flush_nogap will flush the internal mp3 buffers and pad
   * the last frame with ancillary data so it is a complete mp3 frame.
   * 
   * 'mp3buf' should be at least 7200 bytes long
   * to hold all possible emitted data.
   *
   * After a call to this routine, the outputed mp3 data is complete, but
   * you may continue to encode new PCM samples and write future mp3 data
   * to a different file.  The two mp3 files will play back with no gaps
   * if they are concatenated together.
   *
   * This routine will NOT write id3v1 tags into the bitstream.
   *
   * return code = number of bytes output to mp3buf. Can be 0
   */
  
  int flush_sz = 0;

  /*
  flush_sz = lame_encode_flush_nogap(gfp,    // global context handle
                                     mp3buf + val, // pointer to encoded MP3 stream
                                     mp3buf_size - val);  // number of valid octets in this stream
  */

  // info->info("VAL: %d  - FLUSH_SZ: %d - TOTAL: %d", val, flush_sz, (val + flush_sz));

  audio_frame->size = val + flush_sz;

  /*

  int bitrate_kbps[14];
  //  lame_bitrate_kbps(gfp, bitrate_kbps);
  lame_bitrate_hist(gfp, bitrate_kbps);
  // 32 40 48 56 64 80 96 112 128 160 192 224 256 320
  info->info("%d %d %d %d %d %d %d %d %d %d %d %d %d %d",
             bitrate_kbps[0],
             bitrate_kbps[1],
             bitrate_kbps[2],
             bitrate_kbps[3],
             bitrate_kbps[4],
             bitrate_kbps[5],
             bitrate_kbps[6],
             bitrate_kbps[7],
             bitrate_kbps[8],
             bitrate_kbps[9],
             bitrate_kbps[10],
             bitrate_kbps[11],
             bitrate_kbps[12],
             bitrate_kbps[13]);
  */
  //  while(frame_number != lame_get_frameNum(gfp)) {

  calc_bitrate += audio_frame->size;//lame_get_framesize(gfp);
  frame_number ++;//= 1;//lame_get_frameNum(gfp);

    //    info->info("lame_get_frameNum(gfp) %d ?= frame_number %d", lame_get_frameNum(gfp), frame_number);
    //  }

  // Bits pr. second
  // 25 * 7 frames pr.second (it seems!)
  audio_frame->bitrate = (unsigned int)((double)calc_bitrate / (double)(frame_number)) * 25;
  /*
  info->info("Audio size: %d, bitrate: %.4f", 
             audio_frame->bitrate, 
             (float)(config->readInt("mp3_bitrate") * 1000)/(float)(audio_frame->bitrate));
  */

  /*
  FILE* fp = fopen("/tmp/audiotest.mp3", "a");
  fwrite(audio_frame->data, audio_frame->size, 1, fp);
  fclose(fp);
  */
  return audio_frame;
}