/*
* 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 <intrin.h>
#include <memory.h>
#include <xmmintrin.h>
#include "dsdpcm_converter.h"

#ifndef _DSDPCM_CONVERTER_SINGLE_H_INCLUDED
#define _DSDPCM_CONVERTER_SINGLE_H_INCLUDED

#define NORM_S ((double)1 / (double)((uint32_t)1 << 31))

typedef float ctable_s[256];

class dsdpcm_fir_r {
	ctable_s* fir_ctables;
	int       fir_order;
	int       fir_length;
	int       fir_size;
	int       channels;
	int       decimation;
	uint8_t*  fir_buffer[DSDPCM_MAX_CHANNELS];
	int       fir_index;
public:
	dsdpcm_fir_r() {
		memset(fir_buffer, 0, sizeof(fir_buffer));
	}
	~dsdpcm_fir_r() {
		free();
	}
	void init(ctable_s* fir_ctables, int fir_length, int channels, int decimation);
	void free();
	int get_decimation() {
		return decimation;
	}
	float get_delay();
	int run(uint8_t* dsd_data, float* pcm_data, int dsd_samples);
};

class pcmpcm_fir_r {
	float* fir_coefs;
	int    fir_order;
	int    fir_length;
	int    fir_size;
	int    channels;
	int    decimation;
	float* fir_buffer[DSDPCM_MAX_CHANNELS];
	int    fir_index;
public:
	pcmpcm_fir_r() {
		memset(fir_buffer, 0, sizeof(fir_buffer));
	}
	~pcmpcm_fir_r() {
		free();
	}
	void init(float* fir_coefs, int fir_length, int channels, int decimation);
	void free();
	int get_decimation() {
		return decimation;
	}
	float get_delay();
	int run(float* pcm_data, float* out_data, int pcm_samples);
};

class dsdpcm_converter_s : public dsdpcm_conv_impl_t {
	static float dsd_fir1_8_ctables[CTABLES(DSDFIR1_8_LENGTH)][256];
	static float dsd_fir1_16_ctables[CTABLES(DSDFIR1_16_LENGTH)][256];
	static float dsd_fir1_64_ctables[CTABLES(DSDFIR1_64_LENGTH)][256];
	static float pcm_fir2_2_coefs[PCMFIR2_2_LENGTH];
	static float pcm_fir3_2_coefs[PCMFIR3_2_LENGTH];

	ctable_s* dsd_user_fir_ctables;
	int       dsd_user_fir_length;

	float delay;

	dsdpcm_fir_r dsd_fir1;
	pcmpcm_fir_r pcm_fir2a;
	pcmpcm_fir_r pcm_fir2b;
	pcmpcm_fir_r pcm_fir2c;
	pcmpcm_fir_r pcm_fir2d;
	pcmpcm_fir_r pcm_fir3;
	
	float gain;

	float pcm_temp1[DSDPCM_MAX_CHANNELS * (DSDxFs8 / 75)];
	float pcm_temp2[DSDPCM_MAX_CHANNELS * (DSDxFs4 / 75)];
	float pcm_temp3[DSDPCM_MAX_CHANNELS * (DSDxFs4 / 75)];
	float pcm_temp4[DSDPCM_MAX_CHANNELS * (DSDxFs4 / 75)];
	float pcm_temp5[DSDPCM_MAX_CHANNELS * (DSDxFs4 / 75)];
	float pcm_tempo[DSDPCM_MAX_CHANNELS * (DSDxFs8 / 75)];

	bool conv_called;
	
public:
	dsdpcm_converter_s(conv_type_t conv_type);
	~dsdpcm_converter_s();
	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_s* fir_ctables);
	int set_ctables(double* fir_coefs, int fir_length, ctable_s* fir_ctables);
	void set_coefs(const int32_t* int_coefs, int fir_length, float* real_coefs);
	void preinit();
	int convert_internal(uint8_t* dsd_data, float* pcm_data, int dsd_samples);
};

#endif
