/*
* SACD Decoder plugin
* Copyright (c) 2011-2014 Maxim V.Anisiutkin <maxim.anisiutkin@gmail.com>
*
* This program 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 program 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "dsdpcm_converter.h"

#ifndef _DSDPCM_CONVERTER_INTEGER_H_INCLUDED
#define _DSDPCM_CONVERTER_INTEGER_H_INCLUDED

typedef int32_t ctable_i[256];

class dsdpcm_fir_i {
	ctable_i* fir_ctables;
	int       fir_order;
	int       fir_length;
	int       channels;
	int       decimation;
	uint8_t*  fir_buffer;
	int       fir_index;
public:
	void init(ctable_i* fir_ctables, int fir_length, int channels, int decimation, uint8_t* fir_buffer);
	int get_decimation() {
		return decimation;
	}
	float get_delay();
	int run(uint8_t* dsd_data, int32_t* pcm_data, int dsd_samples);
};

class pcmpcm_fir_i {
	int32_t* fir_coefs;
	int      fir_order;
	int      fir_length;
	int      channels;
	int      decimation;
	int      offset;
	int      scale;
	int32_t* fir_buffer;
	int      fir_index;
public:
	void init(int32_t* fir_coefs, int fir_length, int channels, int decimation, int32_t* fir_buffer, int offset = PCMFIR_OFFSET, int scale = PCMFIR_SCALE);
	int get_decimation() {
		return decimation;
	}
	float get_delay();
	int run(int32_t* pcm_data, int32_t* out_data, int pcm_samples);
};

class dsdpcm_converter_i : public dsdpcm_conv_impl_t {
	static int32_t dsd_fir1_8_ctables[CTABLES(DSDFIR1_8_LENGTH)][256];
	static int32_t dsd_fir1_16_ctables[CTABLES(DSDFIR1_16_LENGTH)][256];
	
	float delay;

	dsdpcm_fir_i dsd_fir1;
	pcmpcm_fir_i pcm_fir2a;
	pcmpcm_fir_i pcm_fir2b;
	pcmpcm_fir_i pcm_fir2c;
	pcmpcm_fir_i pcm_fir2d;
	pcmpcm_fir_i pcm_fir3;
	
	uint8_t dsd_fir1_buffer[2 * CTABLES(DSDFIR1_16_LENGTH) * DSDPCM_MAX_CHANNELS];
	int32_t pcm_fir2a_buffer[2 * PCMFIR2_2_LENGTH * DSDPCM_MAX_CHANNELS];
	int32_t pcm_fir2b_buffer[2 * PCMFIR2_2_LENGTH * DSDPCM_MAX_CHANNELS];
	int32_t pcm_fir2c_buffer[2 * PCMFIR2_2_LENGTH * DSDPCM_MAX_CHANNELS];
	int32_t pcm_fir2d_buffer[2 * PCMFIR2_2_LENGTH * DSDPCM_MAX_CHANNELS];
	int32_t pcm_fir3_buffer[2 * PCMFIR3_2_LENGTH * DSDPCM_MAX_CHANNELS];

	int32_t pcm_temp1[DSDPCM_MAX_SAMPLES];
	int32_t pcm_temp2[DSDPCM_MAX_SAMPLES];
	int32_t pcm_temp3[DSDPCM_MAX_SAMPLES];
	int32_t pcm_temp4[DSDPCM_MAX_SAMPLES];
	int32_t pcm_temp5[DSDPCM_MAX_SAMPLES];
	
	int32_t gain;
	int64_t out_minval;
	int64_t out_maxval;

	bool conv_called;

public:
	dsdpcm_converter_i(conv_type_t conv_type);
	~dsdpcm_converter_i();
	int init(int channels, int framerate, int dsd_samplerate, int pcm_samplerate);
	float get_delay();
	bool is_convert_called();
	int convert(uint8_t* dsd_data, int32_t* pcm_data, int dsd_samples);
	int convert(uint8_t* dsd_data, float* pcm_data, int dsd_samples);
	void set_fir(double* fir_coefs, int fir_length);
	void set_gain(float dB_gain);
private:
	int set_ctables(int32_t* fir_coefs, int fir_length, ctable_i* fir_ctables);
	void preinit();
	int convert_internal(uint8_t* dsd_data, int32_t* pcm_data, int dsd_samples);
	int fracmul(int32_t* pcm_data, int32_t* out_data, int pcm_samples);
};

#endif
