/*
			(c) Copyright 1998-2000 - Tord Jansson
			======================================

		This file is part of the BladeEnc MP3 Encoder, based on
		ISO's reference code for MPEG Layer 3 compression, and might
		contain smaller or larger sections that are directly taken
		from ISO's reference code.

		All changes to the ISO reference code herein are either
		copyrighted by Tord Jansson (tord.jansson@swipnet.se)
		or sublicensed to Tord Jansson by a third party.

	BladeEnc is free software; you can redistribute this file
	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.

	2000-03-09  AndrŽ Piotrowski
*/

#include <stdlib.h>
#include "common.h"
#include "encoder.h"
#include "l3psy.h"
#include "loop.h"
#include "l3bitstream.h"
#include "formatbitstream2.h"
#include  "codec.h"
#include "mdct.h"


extern int	fInit_mdct_sub;
extern int	fInit_mdct;

extern void	fixStatic_reservoir();





/************************************************************************/

#define	SAMPLES_PER_FRAME		1152


static	L3SBS					l3_sb_sample;

static	layer					info;

static	FLOAT					buffer[2][2048];
static	int						buffer_idx;

static	int						stereo, error_protection;
static	int						whole_SpF;
static	double					frac_SpF, slot_lag;

static	CodecInitOut			sOut;

static	frame_params			fr_ps;



char					*pEncodedOutput;
int						outputBit;


/* Has to be global because of codecFlush() */

volatile double			avg_slots_per_frame;
static	III_side_info_t	l3_side;


/*____ codecInit() ____________________________________________________________*/

CodecInitOut *codecInit (CodecInitIn *psIn)
{
	int						j;


	/* Read psIn */

	switch (psIn->frequency)
	{
		case 48000:  info.sampling_frequency = 1;  break;
		case 44100:  info.sampling_frequency = 0;  break;
		case 32000:  info.sampling_frequency = 2;  break;
		default   :  return FALSE;
	}

	switch (psIn->mode)
	{
		case  0:  info.mode = MPG_MD_STEREO      ;  info.mode_ext = 0;  break;
		case  2:  info.mode = MPG_MD_DUAL_CHANNEL;  info.mode_ext = 0;  break;
		case  3:  info.mode = MPG_MD_MONO        ;  info.mode_ext = 0;  break;
		default:  return FALSE;
	}

	j = 0;
	while (j < 15  &&  bitratex[1][j] != psIn->bitrate)
		j++;
	info.bitrate_index    = j;

	info.version 	      = 1;   /* Default: MPEG-1 */
	info.emphasis 	      = psIn->emphasis;
	info.extension 	      = psIn->fPrivate;
	info.copyright 	      = psIn->fCopyright;
	info.original 	      = psIn->fOriginal;
	info.error_protection = psIn->fCRC;
	

			
/*_______ Static-fix _______________*/


	fInit_mdct_sub = 0;
	fInit_mdct = 0;

	fixStatic_reservoir();


/*___________________________________*/

	psycho_anal_init (info.sampling_frequency);
	initWindowFilterSubband ();
	initFormatBitstream ();


/*     clear buffers */
	memset ((char *) buffer, 0, sizeof(buffer));  buffer_idx = 0;


	fr_ps.header      = &info;
	fr_ps.tab_num     = -1;             /* no table loaded */
	fr_ps.alloc       = NULL;
	fr_ps.actual_mode = info.mode;
	fr_ps.stereo      = (info.mode == MPG_MD_MONO) ? 1 : 2;
	fr_ps.sblimit     = SBLIMIT;
	fr_ps.jsbound     = SBLIMIT;

    
    stereo = fr_ps.stereo;
    error_protection = info.error_protection;

    avg_slots_per_frame =
    	((double)SAMPLES_PER_FRAME / s_freq[1][info.sampling_frequency]) *
		((double)bitratex[1][info.bitrate_index] / 8.0);
    whole_SpF = (int) avg_slots_per_frame;
    frac_SpF  = avg_slots_per_frame - (double)whole_SpF;
    slot_lag  = -frac_SpF;
    
/*    if (frac_SpF == 0)
    	info.padding = 0;
*/

/*________________________*/


	if( stereo != 2 )
		sOut.nSamples = SAMPLES_PER_FRAME;
	else
		sOut.nSamples = SAMPLES_PER_FRAME*2;

	sOut.bufferSize = 2048;

	return  &sOut;			/* How many samples we want in each chunk... */
}





/*____ codecEncodeChunk() _____________________________________________________*/

unsigned int codecEncodeChunk( int nSamples, short *pSamples, char *pDest )
{
	static	double			xr[2][2][576];
	static	double			pe[2][2];
	static	int				l3_enc[2][2][576];
	static	III_psy_ratio	ratio;
	static	III_scalefac_t 	scalefac;

	int						gr, ch;
	int						mean_bits, sideinfo_len;
	int						bitsPerFrame;
	int						j;


	/* rebuffer audio */

	rebuffer_audio (pSamples, buffer, &buffer_idx, nSamples, stereo);


	/*		psychoacoustic model */

	for (gr=0; gr<2; gr++)
		for (ch=0; ch<stereo; ch++)
		    psycho_anal (buffer[ch], (buffer_idx+gr*576) & 2047, ch, 3, ratio.l[gr][ch], ratio.s[gr][ch], &pe[gr][ch], &l3_side.gr[gr].ch[ch].tt);


	/*		polyphase filtering */

	for (gr=0; gr<2; gr++)
	{
		int gr_plus_1 = gr_idx[gr+1];

		for (ch=0; ch<stereo; ch++)
			for (j=0; j<18; j++)
				windowFilterSubband (buffer[ch], (buffer_idx+768-480+gr*18*32+32*j) & 2047, l3_sb_sample[ch][gr_plus_1][j]);
	}


	/*		apply mdct to the polyphase outputs */

	mdct_sub (&l3_sb_sample, xr, stereo, &l3_side, 2);



	pEncodedOutput = pDest;
	*pEncodedOutput = 0;
	outputBit = 8;



	if (frac_SpF != 0)
	{
		if (slot_lag > (frac_SpF-1.0))
		{
			slot_lag -= frac_SpF;
			info.padding = 0;
		}
		else
		{
			info.padding = 1;
			slot_lag -= (frac_SpF-1.0);
		}
	}

	bitsPerFrame = 8 * whole_SpF + (info.padding * 8);


/*		determine the mean bitrate for main data */

	sideinfo_len = 32;

	if (stereo == 1)
		sideinfo_len += 136;
	else
		sideinfo_len += 256;

	if (info.error_protection)
		sideinfo_len += 16;

	mean_bits = (bitsPerFrame - sideinfo_len) / 2;




/*    bit and noise allocation */

	iteration_loop (pe, xr, &ratio, &l3_side, l3_enc, mean_bits, stereo, &scalefac, &fr_ps, 0, bitsPerFrame);


/*		write the frame to the bitstream */

	III_format_bitstream (bitsPerFrame, &fr_ps, l3_enc, &l3_side, &scalefac, xr, NULL, 0);
	    

	return  pEncodedOutput - pDest;
}





/*____ codecExit() ____________________________________________________________*/

unsigned int codecExit (char *pDest)
{
	pEncodedOutput = pDest;
	*pEncodedOutput = 0;
	outputBit = 8;

	exitFormatBitstream();
 	III_FlushBitstream();

	return  pEncodedOutput - pDest;
}


/*____ codecFlush() _________________________________________________________*/

unsigned int codecFlush( char * pDest )
{
	pEncodedOutput = pDest;
	outputBit = 8;
	pEncodedOutput[0] = 0;

	flushFrame();


    whole_SpF = (int) avg_slots_per_frame;
    frac_SpF  = avg_slots_per_frame - (double)whole_SpF;
    slot_lag  = -frac_SpF;

	l3_side.main_data_begin = 0;

	fixStatic_reservoir();

	return  pEncodedOutput - pDest;
}
