/*
 * pvr350audio.c: 
 *
 * See the README file for copyright information and how to reach the author.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <vdr/tools.h>
#include "pvr350audio.h"

/**** The following functions are coming more or less from a52dec */
static inline int16_t Convert(int32_t i)
{
	if (i > 0x43c07fff)
		return 32767;
	else if (i < 0x43bf8000)
		return -32768;
	else
		return i - 0x43c00000;
}

void cAC3toMP2::float2s16(float *_f, int16_t *s16, int nchannels)
{
	int		i, j;
	int32_t	*f = (int32_t *)_f;	// XX assumes IEEE float format

	for (i = 0; i < 256; i++) {
		for (j = 0; j < nchannels; j++) {
			s16[nchannels * i + j] = Convert(f[i + j * 256]);
		}
	}
}

int cAC3toMP2::convert2wav(sample_t *_samples, int16_t *out_samples, int channels)
{
	float *samples = _samples;
	int out_sample_bytes = (256 * sizeof(int16_t) * channels);

	float2s16(samples, out_samples, channels);
	return out_sample_bytes;
}
/**** end */

cAC3toMP2::cAC3toMP2(AC3DecodeState_t *AC3DecodeStatePtr)
{
	m_Initialized = false;
	disable_adjust = 0;
	disable_dynrng = 0;

	if (!A52DecodeInit(AC3DecodeStatePtr)) {
		esyslog("pvr350: A52 init failed");
		return;
	}

	if (!MP2InitLibrary()) {
		esyslog("pvr350: LAME init failed");
		A52DecodeEnd(AC3DecodeStatePtr);
		return;
	}

	m_AC3DecodeStatePtr = AC3DecodeStatePtr;
	m_Initialized = true;
}

cAC3toMP2::~cAC3toMP2()
{
	A52DecodeEnd(m_AC3DecodeStatePtr);
	MP2CloseLibrary();
	m_Initialized = false;
}

bool cAC3toMP2::A52DecodeInit(AC3DecodeState_t *s)
{
	s->state = a52_init(MM_ACCEL_DJBFFT);
	if (s->state == NULL) {
		return false;
	}
	s->samples = a52_samples(s->state);
	s->inbuf_ptr = s->inbuf;
	s->frame_size = 0;
	m_AC3Channels = AUDIO_CHANNELS;
	return true;
}

void cAC3toMP2::A52DecodeEnd(AC3DecodeState_t *s)
{
	a52_free(s->state);
	s->state = NULL;
}

int cAC3toMP2::A52DecodeFrame(AC3DecodeState_t *s,
			  int16_t *outbuf, int *outbuf_size,
			  uint8_t *inbuf, int inbuf_size)
{
	uint8_t *inbuf_ptr = inbuf;
	int16_t *outbuf_ptr = outbuf;
	int sample_rate, bit_rate;
	int flags, i, len, wav_size;
	float level;
	static const int ac3_channels[8] = {
		2, 1, 2, 3, 3, 4, 4, 5
	};

	if (!m_Initialized) {
		// Libraries not initialized !!!
		return -1;
	}

	*outbuf_size = 0;
	while (inbuf_size > 0) {
		len = s->inbuf_ptr - s->inbuf;	// remaining bytes in input buffer
		if (s->frame_size == 0) {
			// no header seen : find one. We need a least 7 bytes to parse it
			len = AC3_HEADER_SIZE - len;
			if (len > inbuf_size)
				len = inbuf_size;
			memcpy(s->inbuf_ptr, inbuf_ptr, len);	//append data to input buffer
			inbuf_ptr += len;
			s->inbuf_ptr += len;
			inbuf_size -= len;
			if ((s->inbuf_ptr - s->inbuf) == AC3_HEADER_SIZE) {
				len = a52_syncinfo(s->inbuf, &s->flags, &sample_rate, &bit_rate);

				if (len == 0) {
					// no sync found : move by one byte (inefficient, but simple!)
					memcpy(s->inbuf, s->inbuf + 1, AC3_HEADER_SIZE - 1);
					s->inbuf_ptr--;
				} else {
					s->frame_size = len;	// AC3 frame length
					// update codec info
					s->sample_rate = sample_rate;
					s->channels = ac3_channels[s->flags & 7];
					if (s->flags & A52_LFE)
						s->channels++;
					if (m_AC3Channels == 0) {
						m_AC3Channels = s->channels;
					} else if (s->channels < m_AC3Channels) {
						esyslog("pvr350: AC3 source channels are less than specified");
						m_AC3Channels = s->channels;
					}
					s->bit_rate = bit_rate;
				}
			}
		} else if (len < s->frame_size) {
			len = s->frame_size - len;
			if (len > inbuf_size)
				len = inbuf_size;
			memcpy(s->inbuf_ptr, inbuf_ptr, len);
			inbuf_ptr += len;
			s->inbuf_ptr += len;
			inbuf_size -= len;
		} else {
			flags = A52_DOLBY;
			if (!disable_adjust)
				flags |= A52_ADJUST_LEVEL;
			level = 1;
			if (a52_frame(s->state, s->inbuf, &flags, &level, BIAS)) {
				esyslog("pvr350: Error decoding frame");
fail:
				s->inbuf_ptr = s->inbuf;
				s->frame_size = 0;
				continue;
			}
			if (disable_dynrng)
				a52_dynrng(s->state, NULL, NULL);

			wav_size = 0;
			for (i = 0; i < 6; i++) {
				int bytes;
				if (a52_block(s->state)) {
					esyslog("pvr350: Error decoding block");
					goto fail;
				}
				bytes = convert2wav(s->samples, outbuf_ptr, m_AC3Channels);
				wav_size += bytes;
				outbuf_ptr += (bytes / 2);	// Pointer to int16_t (short)
			}
			s->inbuf_ptr = s->inbuf;
			s->frame_size = 0;
			*outbuf_size = wav_size;
			break;
		}
	}
	return inbuf_ptr - inbuf;
}

void cAC3toMP2::MP2CloseLibrary()
{
	twolame_close(&encodeOptions);
	encodeOptions = NULL;
}

bool cAC3toMP2::MP2InitLibrary()
{
        encodeOptions = twolame_init();
	// Set the samplerate of the PCM audio input. (Default: 44100)
	twolame_set_in_samplerate(encodeOptions, AC3_SAMPLERATE);
	// Set the samplerate of the MPEG audio output. (Default: 44100)
	twolame_set_out_samplerate(encodeOptions, AC3_SAMPLERATE);
	// Set the bitrate of the MPEG audio output stream. (Default: 192)
	twolame_set_bitrate(encodeOptions, MP2_BITRATE);
	// Set the number of channels in the input stream.
	twolame_set_num_channels(encodeOptions, AUDIO_CHANNELS);

	if (twolame_init_params(encodeOptions) != 0) {
		MP2CloseLibrary();
		return false;
	} else {
		return true;
	}
}

int cAC3toMP2::MP2EncodeFrame(int16_t *inbuf, int inbuf_bytes, uint8_t *outbuf)
{
	int mp2_len = 0;
	int num_samples = (inbuf_bytes / sizeof(int16_t) / m_AC3Channels);

	if (!m_Initialized) {
		// Libraries not initialized !!!
		return -1;
	}

	if (inbuf) {
		if (m_AC3Channels != AUDIO_CHANNELS) {
			mp2_len = twolame_encode_buffer(encodeOptions,
							inbuf,
							inbuf,
							num_samples,
							outbuf,
							MP2_PES_DATA_SIZE);
		} else {
			mp2_len = twolame_encode_buffer_interleaved(encodeOptions,
							inbuf,
							num_samples,
							outbuf,
							MP2_PES_DATA_SIZE);
		}
	} else {
		mp2_len = twolame_encode_flush(encodeOptions,
						outbuf,
					 	MP2_PES_DATA_SIZE);
	}

	if (mp2_len < 0) {
		esyslog("pvr350: TwoLAME output buffer too small");
	}

	return mp2_len;
}

