/*
* SACD Decoder plugin
* Copyright (c) 2011-2014 Maxim V.Anisiutkin <maxim.anisiutkin@gmail.com>
* Optimization update Damien Plisson <damien78@audirvana.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
*/

#ifdef _WIN32
#include <intrin.h>
#endif
#include <xmmintrin.h>
#include "dsdpcm_converter.h"

#ifndef _DSDPCM_CONVERTER_DOUBLE_H_INCLUDED
#define _DSDPCM_CONVERTER_DOUBLE_H_INCLUDED

#define NORM_D ((double)1 / (double)((uint32_t)1 << 31))

typedef double ctable_d[256];
typedef double dsdpcm_pcmtemp_d[DSDPCM_MAX_CHANNELS][DSDxFs128 / 8 / 75];

class dsdpcm_fir_d {
	ctable_d* 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_d() {
		memset(fir_buffer, 0, sizeof(fir_buffer));
	}
	~dsdpcm_fir_d() {
		free();
	}
	void init(ctable_d* 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, dsdpcm_pcmtemp_d pcm_data, int dsd_samples);
};

class pcmpcm_fir_d {
	double* fir_coefs;
	int     fir_order;
	int     fir_length;
	int     fir_size;
	int     channels;
	int     decimation;
	double* fir_buffer[DSDPCM_MAX_CHANNELS];
	int     fir_index;
public:
	pcmpcm_fir_d() {
		memset(fir_buffer, 0, sizeof(fir_buffer));
	}
	~pcmpcm_fir_d() {
		free();
	}
	void init(double* fir_coefs, int fir_length, int channels, int decimation);
	void free();
	int get_decimation() {
		return decimation;
	}
	float get_delay();
	int run(dsdpcm_pcmtemp_d pcm_data, dsdpcm_pcmtemp_d out_data, int pcm_samples);
};

class dsdpcm_converter_d : public dsdpcm_conv_impl_t {
	static double dsd_fir1_8_ctables[CTABLES(DSDFIR1_8_LENGTH)][256];
	static double dsd_fir1_16_ctables[CTABLES(DSDFIR1_16_LENGTH)][256];
	static double dsd_fir1_64_ctables[CTABLES(DSDFIR1_64_LENGTH)][256];
	static double pcm_fir2_2_coefs[PCMFIR2_2_LENGTH];
	static double pcm_fir3_2_coefs[PCMFIR3_2_LENGTH];

	ctable_d* dsd_user_fir_ctables;
	int       dsd_user_fir_length;

	float delay;

	dsdpcm_fir_d dsd_fir1;
	pcmpcm_fir_d pcm_fir2a;
	pcmpcm_fir_d pcm_fir2b;
	pcmpcm_fir_d pcm_fir2c;
	pcmpcm_fir_d pcm_fir2d;
	pcmpcm_fir_d pcm_fir3;
	
	double gain;

	dsdpcm_pcmtemp_d pcm_temp1;
	dsdpcm_pcmtemp_d pcm_temp2;
	dsdpcm_pcmtemp_d pcm_temp3;
	dsdpcm_pcmtemp_d pcm_temp4;
	dsdpcm_pcmtemp_d pcm_temp5;
	dsdpcm_pcmtemp_d pcm_tempo;

	bool conv_called;
	
public:
	dsdpcm_converter_d(conv_type_t conv_type);
	virtual ~dsdpcm_converter_d();
	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);
	int convert(uint8_t* dsd_data, double* 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_d* fir_ctables);
	int set_ctables(double* fir_coefs, int fir_length, ctable_d* fir_ctables);
	void set_coefs(const int32_t* int_coefs, int fir_length, double* real_coefs);
	void preinit();
	int convert_internal(uint8_t* dsd_data, dsdpcm_pcmtemp_d pcm_data, int dsd_samples);
};

#endif
