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

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

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



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<math.h>

#include	"archi.h"
#include	"base_ct.h"
#include	"log.h"
#include	"memory.h"
#include	"Sample.h"
#include	"WaveForm.h"
#include	"WaveForm.hpp"



/*\\\ CONSTANTES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ CONSTANTES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ TYPES & STRUCTURES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\*/

WaveForm	*WaveForm::base_ptr [WaveForm_NBR_BASE_FORMS];

const char	*const WaveForm::name_0_ptr [WaveForm_NBR_BASE_FORMS] =
{
	"Sine", "Square", "Triangle", "Ramp up", "Ramp down", "Random"
};



/*\\\ DEFINITION DES VARIABLES DE CLASSE PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ METHODES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise une forme d'onde en lui reservant de la     */
/*                   memoire.                                               */
/*      Parametres en entree:                                               */
/*        - p2length: puissance de 2 de la longueur de la table, en samples */
/*        - loop_type: bouclage par defaut de la table                      */
/*        - interpolation_flag: true si les valeurs sont interpolees par    */
/*                              defaut.                                     */
/*==========================================================================*/

WaveForm::WaveForm (int p2length, int loop_type, bool interpolation_flag)
{
	if (   p2length < 0
	    || p2length > 32)
	{
		_length = 0;
		return;
	}

	_length = 1L << p2length;
	_mask = _length - 1;
	_loop_type = loop_type;
	_interpolation_flag = interpolation_flag;
	_table_ptr = (double *) MALLOC (_length * sizeof (*_table_ptr));
	if (_table_ptr != NULL)
	{
		memset (_table_ptr, 0, _length * sizeof (*_table_ptr));
	}
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Detruit                                                */
/*==========================================================================*/

WaveForm::~WaveForm ()
{
	if (_table_ptr != NULL)
	{
		FREE (_table_ptr);
		_table_ptr = NULL;
	}
}



/*==========================================================================*/
/*      Nom: check_ok                                                       */
/*      Description: Verifie l'intergrite de l'objet.                       */
/*      Retour: 0 si l'objet parait correct.                                */
/*==========================================================================*/

signed int	WaveForm::check_ok (void) const
{
	if (_length <= 0)
	{
		LOG_printf ("WaveForm::check_ok: Error: array length is 0.\n");
		return (-1);
	}

	if (_table_ptr == NULL)
	{
		LOG_printf ("WaveForm::check_ok: Error: _table_ptr is NULL.\n");
		return (-1);
	}

	MCHECK (_table_ptr);

	return (0);
}



/*==========================================================================*/
/*      Nom: self_display                                                   */
/*      Description: Affiche "textuellement" le contenu courant de l'objet. */
/*==========================================================================*/

void	WaveForm::self_display (void) const
{

	/*** A faire ***/

}



double	WaveForm::get_point (signed long pos_int, double pos_frac) const
{
	double	p1;
	double	p2;

	/* Pas de bouclage */
	if (_loop_type == WaveForm_LOOP_TYPE_NONE)
	{
		if (pos_int < 0)
		{
			p1 = _table_ptr [0];
		}
		else if (pos_int > _mask)
		{
			p1 = _table_ptr [_mask];
		}
		else
		{
			p1 = _table_ptr [pos_int];
		}

		if (! _interpolation_flag)
		{
			return (p1);
		}

		/* Interpolation: on prend un deuxieme point */
		pos_int ++;
		if (pos_int < 0)
		{
			p2 = _table_ptr [0];
		}
		else if (pos_int > _mask)
		{
			p2 = _table_ptr [_mask];
		}
		else
		{
			p1 = _table_ptr [pos_int];
		}
	}

	/* Avec bouclage */
	else
	{
		p1 = _table_ptr [pos_int & _mask];

		if (! _interpolation_flag)
		{
			return (p1);
		}

		/* Interpolation: on prend un deuxieme point */
		p2 = _table_ptr [(pos_int + 1) & _mask];
	}

	return (p1 + (p2 - p1) * pos_frac);
}



signed int	WaveForm::init (void)
{
	int		wf_cnt;
	long		point_cnt;

	for (wf_cnt = 0; wf_cnt < WaveForm_NBR_BASE_FORMS; wf_cnt ++)
	{
		base_ptr [wf_cnt] = new WaveForm (WaveForm_BASE_LENGTH_P2, WaveForm_LOOP_TYPE_FWD, true);
		if (base_ptr [wf_cnt] == NULL)
		{
			LOG_printf ("WaveForm::init: Error: couldn't create base waveform %d.\n", wf_cnt);
			return (-1);
		}
	}

	for (point_cnt = 0; point_cnt < WaveForm_BASE_LENGTH; point_cnt ++)
	{
		base_ptr [WaveForm_BASE_SIN]->_table_ptr [point_cnt] =
			sin (point_cnt * 2 * PI / WaveForm_BASE_LENGTH);

		base_ptr [WaveForm_BASE_SQUARE]->_table_ptr [point_cnt] =
			(point_cnt < WaveForm_BASE_LENGTH / 2) ? 1.0 : -1.0;

		base_ptr [WaveForm_BASE_RAMP_UP]->_table_ptr [point_cnt] =
			(double)point_cnt * 2 / (WaveForm_BASE_LENGTH - 1) - 1.0;
		
		base_ptr [WaveForm_BASE_RAMP_DOWN]->_table_ptr [point_cnt] =
			- base_ptr [WaveForm_BASE_RAMP_UP]->_table_ptr [point_cnt];
		
		base_ptr [WaveForm_BASE_RANDOM]->_table_ptr [point_cnt] =
			(double)rand () * 2 / RAND_MAX - 1;
		
		if (point_cnt <= WaveForm_BASE_LENGTH / 4)
		{
			base_ptr [WaveForm_BASE_TRIANGLE]->_table_ptr [point_cnt] =
				(double)point_cnt / (WaveForm_BASE_LENGTH / 4);
		}
		else if (point_cnt < WaveForm_BASE_LENGTH / 2)
		{
			base_ptr [WaveForm_BASE_TRIANGLE]->_table_ptr [point_cnt] =
				base_ptr [WaveForm_BASE_TRIANGLE]->_table_ptr [WaveForm_BASE_LENGTH / 2 - point_cnt];
		}
		else
		{
			base_ptr [WaveForm_BASE_TRIANGLE]->_table_ptr [point_cnt] =
				- base_ptr [WaveForm_BASE_TRIANGLE]->_table_ptr [point_cnt - WaveForm_BASE_LENGTH / 2];
		}
	}

	return (0);
}



void	WaveForm::restore (void)
{
	int		wf_cnt;

	for (wf_cnt = 0; wf_cnt < WaveForm_NBR_BASE_FORMS; wf_cnt ++)
	{
		if (base_ptr [wf_cnt] != NULL)
		{
			delete base_ptr [wf_cnt];
			base_ptr [wf_cnt] = NULL;
		}
	}
}



/*\\\ METHODES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



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



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/
