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

GRAOUMF TRACKER 2

Copyright (c) 1996 - 2002 Laurent de Soras

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Contact the author : laurent@ohmforce.com
More information about this license : http://www.gnu.org/licenses/gpl.html

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



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

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

#include	"archi.h"
#include	"base.h"
#include	"base.hpp"
#include	"base_ct.h"
#include	"filter.h"
#include	"FxPreset.h"
#include	"gtracker.h"
#include	"log.h"
#include	"memory.h"
#include	"mixer.h"
#include	"Player.h"
#include	"tracks.h"
#include	"Sample.h"
#include	"WaveForm.h"
#include	"WaveForm.hpp"



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

const long	Player::_fx_delay_buffer_granul = 1L << 15;



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



/*\\\ PROTOTYPES DES FONCTIONS PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

UBYTE	Player::_fx_command_format_ptr [FxPreset_NBR_TYPES] [0x100];	// Default to TRK_EXT_TRACK_TYPE_FX_2D



/*\\\ VARIABLES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

const Player::TrackFncPtr	Player::_fx_general_command_ptr [0x40] [2] =
{
	/* C0 - CF */
	{ NULL, NULL },
	{ &Player::fx_general_add_track, NULL },
	{ &Player::fx_general_remove_track, NULL },
	{ &Player::fx_general_apply_preset, NULL },
	{ &Player::fx_general_remove_fx, NULL },
	{ &Player::fx_general_mute_input_tracks, NULL },
	{ &Player::fx_general_set_stereo, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },

	/* D0 - DF */
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },

	/* E0 - EF */
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },

	/* F0 - FF */
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL },
	{ NULL, NULL }
};

Player::TrackFncPtrFxCmdArr Player::_fx_effect_command_ptr [FxPreset_NBR_TYPES] = { 0 };

const Player::TrackTypeAddDel	Player::_fx_track_type_list [4] =
{
	{ Pattern_TYPE_SPL, true },
	{ Pattern_TYPE_FX, true },
	{ Pattern_TYPE_FX, false },
	{ Pattern_TYPE_AIN, true },
};



/*\\\ FONCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: fx_init                                                    */
/*      Description: Initialise les variables et fonctions qui touchent aux */
/*                   commandes d'effets.                                    */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

int	Player::fx_init (void)
{
	int		cmd_cnt_2;
	int		fnc_cnt;

	for (int fx_type = 0; fx_type < FxPreset_NBR_TYPES; ++fx_type)
	{
		for (int val = 0; val < 0x100; ++val)
		{
			_fx_command_format_ptr [fx_type] [val] = TRK_EXT_TRACK_TYPE_FX_2D;
		}
	}

	/* Les routines qui installent un effet (du type 0x??0?) doivent
	   obligatoirement virer l'ancien effet s'il y en avait un. */
	const struct
	{
		long			command;
		TrackFncPtr	fnc_ptr [2];	// 1st tick, all ticks
		int			format;
	}			functions [] =
	{
		/* Delay */
		{ 0x0101, { &Player::fx_delay_activate, NULL }, -1 },
		{ 0x0110 + FX_TIME_PARAMETER, { &Player::fx_delay_modify_time_1st_tick, &Player::fx_delay_modify_time_all_ticks } },
		{ 0x0120 + FX_DOUBLE_PARAMETER_2D, { &Player::fx_delay_modify_feedback_1st_tick, &Player::fx_delay_modify_feedback_all_ticks } },

		/* Filtre resonnant */
		{ 0x0201, { &Player::fx_resfilt_activate, NULL }, TRK_EXT_TRACK_TYPE_FX_4H },
		{ 0x0210 + FX_FREQ_PARAMETER, { &Player::fx_resfilt_modify_freq_1st_tick, &Player::fx_resfilt_modify_freq_all_ticks } },
		{ 0x0220 + FX_DOUBLE_PARAMETER_2D, { &Player::fx_resfilt_modify_q_1st_tick, &Player::fx_resfilt_modify_q_all_ticks } },
		{ 0x0230 + FX_INT_PARAMETER, { &Player::fx_resfilt_modify_nbr_biq_1st_tick, &Player::fx_resfilt_modify_nbr_biq_all_ticks } },
		{ 0x0240 + FX_FREQ_PARAMETER, { &Player::fx_resfilt_modify_lfo_freq_1st_tick, &Player::fx_resfilt_modify_lfo_freq_all_ticks } },
		{ 0x0245, { &Player::fx_resfilt_set_lfo_waveform, NULL }, TRK_EXT_TRACK_TYPE_FX_INT },
		{ 0x0246, { &Player::fx_resfilt_set_lfo_phase, NULL },  TRK_EXT_TRACK_TYPE_FX_2D },
		{ 0x0250 + FX_DOUBLE_PARAMETER_3D, { &Player::fx_resfilt_modify_lfo_depth_1st_tick, &Player::fx_resfilt_modify_lfo_depth_all_ticks } },
		{ 0x0260 + FX_DOUBLE_PARAMETER_3D, { &Player::fx_resfilt_modify_cell_ratio_1st_tick, &Player::fx_resfilt_modify_cell_ratio_all_ticks } },

		/* Distorsion */
		{ 0x301, { &Player::fx_disto_activate, NULL }, TRK_EXT_TRACK_TYPE_FX_4H },
		{ 0x310 + FX_DOUBLE_PARAMETER_3D, { &Player::fx_disto_modify_gain_1st_tick, &Player::fx_disto_modify_gain_all_ticks } },
		{ 0x320 + FX_DOUBLE_PARAMETER_3D, { &Player::fx_disto_modify_sec_gain_1st_tick, &Player::fx_disto_modify_sec_gain_all_ticks } },
		{ 0x330 + FX_DOUBLE_PARAMETER_3D, { &Player::fx_disto_modify_threshold_1st_tick, &Player::fx_disto_modify_threshold_all_ticks } },

		/* Fin des donnees */
		{ 0x0000 }
	};

	for (int cmd_cnt = 0; ; cmd_cnt ++)
	{
		if (functions [cmd_cnt].command == 0)
		{
			break;
		}

		const int		fx = (int) (functions [cmd_cnt].command >> 8) &0xFF;
		const int		cmd = (int) functions [cmd_cnt].command & 0xFF;
		const int		type = functions [cmd_cnt].command & ~0xFFFFL;
		switch (type)
		{

		case	FX_TIME_PARAMETER:
			for (fnc_cnt = 0; fnc_cnt < 2; fnc_cnt ++)
			{
				for (cmd_cnt_2 = 0x0; cmd_cnt_2 <= 0xF; cmd_cnt_2 ++)
				{
					if (   cmd_cnt_2 <= 4
					    || cmd_cnt_2 >= 7)
					{
						_fx_effect_command_ptr [fx] [cmd + cmd_cnt_2] [fnc_cnt] = functions [cmd_cnt].fnc_ptr [fnc_cnt];
					}
				}
			}

			for (cmd_cnt_2 = 0x8; cmd_cnt_2 <= 0xC; cmd_cnt_2 ++)
			{
				_fx_command_format_ptr [fx] [cmd + cmd_cnt_2] = TRK_EXT_TRACK_TYPE_FX_3D;
			}
			_fx_command_format_ptr [fx] [cmd + 0x7] = TRK_EXT_TRACK_TYPE_FX_N2H;
			_fx_command_format_ptr [fx] [cmd + 0xD] = TRK_EXT_TRACK_TYPE_FX_N2H;
			_fx_command_format_ptr [fx] [cmd + 0xE] = TRK_EXT_TRACK_TYPE_FX_N2H;
			break;

		case	FX_FREQ_PARAMETER:
			for (fnc_cnt = 0; fnc_cnt < 2; fnc_cnt ++)
			{
				for (cmd_cnt_2 = 0x0; cmd_cnt_2 <= 0xF; cmd_cnt_2 ++)
				{
					if (   cmd_cnt_2 <= 0x4
					    || cmd_cnt_2 >= 0xD
						 || cmd_cnt_2 == 0x7
						 || cmd_cnt_2 == 0x8)
					{
						_fx_effect_command_ptr [fx] [cmd + cmd_cnt_2] [fnc_cnt] = functions [cmd_cnt].fnc_ptr [fnc_cnt];
					}
				}
				_fx_effect_command_ptr [fx] [cmd + 0x8] [fnc_cnt] = functions [cmd_cnt].fnc_ptr [fnc_cnt];
			}

			_fx_command_format_ptr [fx] [cmd + 0x0] = TRK_EXT_TRACK_TYPE_FX_INT;
			_fx_command_format_ptr [fx] [cmd + 0x1] = TRK_EXT_TRACK_TYPE_FX_INT;
			_fx_command_format_ptr [fx] [cmd + 0x2] = TRK_EXT_TRACK_TYPE_FX_INT;
			_fx_command_format_ptr [fx] [cmd + 0x3] = TRK_EXT_TRACK_TYPE_FX_INT;
			_fx_command_format_ptr [fx] [cmd + 0x4] = TRK_EXT_TRACK_TYPE_FX_INT;
			_fx_command_format_ptr [fx] [cmd + 0x7] = TRK_EXT_TRACK_TYPE_FX_N2H;
			_fx_command_format_ptr [fx] [cmd + 0x8] = TRK_EXT_TRACK_TYPE_FX_3D;
			_fx_command_format_ptr [fx] [cmd + 0xD] = TRK_EXT_TRACK_TYPE_FX_N2H;
			_fx_command_format_ptr [fx] [cmd + 0xE] = TRK_EXT_TRACK_TYPE_FX_N2H;
			break;

		case	FX_DOUBLE_PARAMETER_2D:
			for (fnc_cnt = 0; fnc_cnt < 2; fnc_cnt ++)
			{
				for (cmd_cnt_2 = 0x0; cmd_cnt_2 <= 0x4; cmd_cnt_2 ++)
				{
					_fx_effect_command_ptr [fx] [cmd + cmd_cnt_2] [fnc_cnt] = functions [cmd_cnt].fnc_ptr [fnc_cnt];
				}
			}
			break;

		case	FX_DOUBLE_PARAMETER_3D:
			for (fnc_cnt = 0; fnc_cnt < 2; fnc_cnt ++)
			{
				for (cmd_cnt_2 = 0x0; cmd_cnt_2 <= 0x4; cmd_cnt_2 ++)
				{
					_fx_effect_command_ptr [fx] [cmd + cmd_cnt_2] [fnc_cnt] = functions [cmd_cnt].fnc_ptr [fnc_cnt];
					_fx_command_format_ptr [fx] [cmd + cmd_cnt_2] = TRK_EXT_TRACK_TYPE_FX_3D;
				}
			}
			break;

		case	FX_INT_PARAMETER:
			for (fnc_cnt = 0; fnc_cnt < 2; fnc_cnt ++)
			{
				for (cmd_cnt_2 = 0x0; cmd_cnt_2 <= 0x4; cmd_cnt_2 ++)
				{
					_fx_effect_command_ptr [fx] [cmd + cmd_cnt_2] [fnc_cnt] = functions [cmd_cnt].fnc_ptr [fnc_cnt];
					_fx_command_format_ptr [fx] [cmd + cmd_cnt_2] = TRK_EXT_TRACK_TYPE_FX_INT;
				}
			}
			break;

		default:
			_fx_effect_command_ptr [fx] [cmd] [0] = functions [cmd_cnt].fnc_ptr [0];
			_fx_effect_command_ptr [fx] [cmd] [1] = functions [cmd_cnt].fnc_ptr [1];
			if (functions [cmd_cnt].format >= 0)
			{
				_fx_command_format_ptr [fx] [cmd] = functions [cmd_cnt].format;
			}
			break;
		
		}
	}

	return (0);
}



/*==========================================================================*/
/*      Nom:fx_restore                                                  */
/*      Description: Restore les initialisations des effets.                */
/*==========================================================================*/

void	Player::fx_restore (void)
{
	// Nothing
}



/****************************************************************************/
/*                                                                          */
/*      ROUTINES GENERALES                                                  */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: fx_fx_set_preset                                           */
/*      Description: Fixe un nouveau preset d'effet sur la piste. Comme la  */
/*                   routine peut etre appelee entre deux ticks, il est     */
/*                   necessaire de regarder si on doit faire une update     */
/*                   apres l'appel.                                         */
/*      Parametres en entree:                                               */
/*        - conf: configuration de l'effet.                                 */
/*        - effect_type: type de l'effet. Il peut etre nul (aucun effet).   */
/*        - auto_val: champ de bit precisant quelles variables temps reel   */
/*                    doivent etre explicitement modifiees lors d'une mise  */
/*                    a jour d'un effet. Les autres valeurs modifiees       */
/*                    automatiquement par l'effet en temps reel sont        */
/*                    recopiee a partir du preset precedent.                */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::fx_fx_set_preset (TrackInfo &track_info, const FxPreset_EFFECT_CONF &conf, int effect_type, long auto_val)
{
	if (effect_type != track_info.mix.fx.effect)
	{
		/* Vire l'ancien effet */
		fx_fx_restore (track_info);

		/* Installe le nouveau */
		track_info.mix.fx.conf = conf;
		track_info.mix.fx.effect = effect_type;
		fx_fx_init (track_info);
	}

	else
	{
		FxPreset::copy_preset (track_info.mix.fx.conf, conf, effect_type, auto_val);
		track_info.mix.fx.update_flag = true;
	}
}



/*==========================================================================*/
/*      Nom: fx_fx_get_preset                                           */
/*      Description: Renvoie les parametres de l'effet courant d'un piste.  */
/*      Parametres en sortie:                                               */
/*        - conf: configuration de l'effet.                                 */
/*        - effect_type: type de l'effet. Il peut etre nul (aucun effet).   */
/*      Parametres en entree:                                               */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::fx_fx_get_preset (const TrackInfo &track_info, FxPreset_EFFECT_CONF &conf, int &effect_type) const
{
	effect_type = track_info.mix.fx.effect;
	conf = track_info.mix.fx.conf;
}



/*==========================================================================*/
/*      Nom: fx_fx_set_default_parameters                               */
/*      Description: Fixe les parametres par defaut d'un effet. Cette       */
/*                   routine ne fait que rediriger le programme sur le bon  */
/*                   effet. Elle ne fait aucun test de validite et ne doit  */
/*                   etre appelee que quand tout va bien.                   */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::fx_fx_set_default_parameters (TrackInfo &track_info)
{
	switch (track_info.mix.fx.effect)
	{
	case	FxPreset_TYPE_DELAY:
		fx_delay_set_default_parameters (track_info);
		break;
	case	FxPreset_TYPE_RESFILT:
		fx_resfilt_set_default_parameters (track_info);
		break;
	case	FxPreset_TYPE_DISTO:
		fx_disto_set_default_parameters (track_info);
		break;
	}
}



/*==========================================================================*/
/*      Nom: fx_fx_init                                                 */
/*      Description: Initialise un effet. Aucun effet ne doit etre encore   */
/*                   actif sur la piste. Les parametres doivent deja etre   */
/*                   initialises.                                           */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

int	Player::fx_fx_init (TrackInfo &track_info)
{
	switch (track_info.mix.fx.effect)
	{
	case	FxPreset_TYPE_DELAY:
		return (fx_delay_init (track_info));
		break;
	case	FxPreset_TYPE_RESFILT:
		return (fx_resfilt_init (track_info));
		break;
	case	FxPreset_TYPE_DISTO:
		return (fx_disto_init (track_info));
		break;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: fx_fx_set_stereo                                           */
/*      Description: Fixe la stereo d'une piste d'effet. C'est en fait a    */
/*                   l'effet d'accepter ou d'imposer sa stereo. Ne pas      */
/*                   oublier de faire les tests de validite.                */
/*      Parametres en entree:                                               */
/*        - stereo: nombre de voies demandees.                              */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*      Retour: Le nombre de voies que l'effet a bien voulu prendre (ou a   */
/*              impose), ou un nombre negatif en cas d'erreur.              */
/*==========================================================================*/

int	Player::fx_fx_set_stereo (TrackInfo &track_info, int stereo)
{
	switch (track_info.mix.fx.effect)
	{
	case	FxPreset_TYPE_NONE:
		track_info.stereo = stereo;
		break;
	case	FxPreset_TYPE_DELAY:
		return (fx_delay_set_stereo (track_info, stereo));
		break;
	case	FxPreset_TYPE_RESFILT:
		return (fx_resfilt_set_stereo (track_info, stereo));
		break;
	case	FxPreset_TYPE_DISTO:
		return (fx_disto_set_stereo (track_info, stereo));
		break;
	}

	return (track_info.stereo);
}



/*==========================================================================*/
/*      Nom: fx_fx_silence                                              */
/*      Description: Reduit au silence tous les buffers de l'effet courant, */
/*                   de sorte qu'une entree de niveau nul produise une      */
/*                   sortie nulle (si bien sur l'effet peut fonctionner     */
/*                   ainsi).                                                */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

int	Player::fx_fx_silence (TrackInfo &track_info)
{
	if (track_info.mix.fx.bad_init_flag)
	{
		return (0);
	}

	switch (track_info.mix.fx.effect)
	{
	case	FxPreset_TYPE_DELAY:
		return (fx_delay_silence (track_info));
		break;
	case	FxPreset_TYPE_RESFILT:
		return (fx_resfilt_silence (track_info));
		break;
	case	FxPreset_TYPE_DISTO:
		return (fx_disto_silence (track_info));
		break;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: fx_fx_update                                               */
/*      Description: Met a jour les parametres d'un effet. S'il n'y a pas   */
/*                   eu de probleme, le flag d'update est retire par les    */
/*                   routines appelees dans ce sous-programme. Si par       */
/*                   contre un probleme est rencontre, l'effet doit etre    */
/*                   reinitialise ulterieurement.                           */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

int	Player::fx_fx_update (TrackInfo &track_info)
{
	if (track_info.mix.fx.bad_init_flag)
	{
		return (0);
	}

	switch (track_info.mix.fx.effect)
	{
	case	FxPreset_TYPE_DELAY:
		return (fx_delay_update (track_info));
		break;
	case	FxPreset_TYPE_RESFILT:
		return (fx_resfilt_update (track_info));
		break;
	case	FxPreset_TYPE_DISTO:
		return (fx_disto_update (track_info));
		break;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: fx_fx_restore                                              */
/*      Description: Libere les buffers d'un effet.                         */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::fx_fx_restore (TrackInfo &track_info)
{
	if (track_info.mix.fx.effect == FxPreset_TYPE_NONE)
	{
		return;
	}

	if (track_info.mix.fx.bad_init_flag)
	{
		track_info.mix.fx.bad_init_flag = false;
	}

	else
	{
		switch (track_info.mix.fx.effect)
		{
		case	FxPreset_TYPE_DELAY:
			fx_delay_restore (track_info);
			break;
		case	FxPreset_TYPE_RESFILT:
			fx_resfilt_restore (track_info);
			break;
		case	FxPreset_TYPE_DISTO:
			fx_disto_restore (track_info);
			break;
		}
	}

	track_info.mix.fx.effect = FxPreset_TYPE_NONE;
}



/*==========================================================================*/
/*      Nom: fx_manage_1st_tick                                         */
/*      Description: Gere les commandes d'effet qui n'agissent que pendant  */
/*                   le premier tick d'une ligne.                           */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::fx_manage_1st_tick (TrackInfo &track_info)
{
	ULWORD	fx;
	int		command;
	int		sub_command;
	int		volume;
	LWORD		parameter;

	track_info.mix.fx.old_effect = track_info.mix.fx.effect;
	
	fx = track_info.score.score.fx_data;
	command = (int) (fx >> 24);
	sub_command = (int) (fx >> 16) & 0xFF;
	parameter = (LWORD) (fx & 0xFFFF);

	/* Volume */
	volume = track_info.score.score.volume;
	if (volume > 0)
	{
		set_lin_velocity (track_info, (volume - 0x10) << 5);
	}

	/* Commandes generales */
	if (command >= 0xC0)
	{
		const TrackFncPtr	fnc_ptr =
			_fx_general_command_ptr [command - 0xC0] [0];
		if (fnc_ptr != NULL)
		{
			track_info.score.score.fx_par = parameter;
			(this->*fnc_ptr) (track_info);
		}
	}

	/* Commandes particulieres a chaque effet */
	else
	{
		/* Si la commande est associee a l'effet courant ou que la commande est
		   un changement d'effet, on la prend. Sinon, on l'ignore. */
		if (   (   command == track_info.mix.fx.effect
		        || (sub_command & 0xF0) == 0x00)
		    && command < FxPreset_NBR_TYPES)
		{
			const TrackFncPtr	fnc_ptr = 
				_fx_effect_command_ptr [command] [sub_command] [0];
			if (fnc_ptr != NULL)
			{
				track_info.score.score.fx_par = parameter;
				(this->*fnc_ptr) (track_info);
			}
		}
	}
}



/*==========================================================================*/
/*      Nom: fx_manage_all_ticks                                        */
/*      Description: Gere les commandes d'effet qui agissent pendant tous   */
/*                   les ticks d'une ligne.                                 */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*      Retour:                                                             */
/*==========================================================================*/

void	Player::fx_manage_all_ticks (TrackInfo &track_info)
{
	ULWORD	fx;
	int		command;
	int		sub_command;
	LWORD		parameter;

	fx = track_info.score.score.fx_data;
	command = (int) (fx >> 24);
	sub_command = (int) (fx >> 16) & 0xFF;
	parameter = (LWORD) (fx & 0xFFFF);

	/* Commandes generales */
	if (command >= 0xC0)
	{
		const TrackFncPtr	fnc_ptr =
			_fx_general_command_ptr [command - 0xC0] [1];
		if (fnc_ptr != NULL)
		{
			track_info.score.score.fx_par = parameter;
			(this->*fnc_ptr) (track_info);
		}
	}

	/* Commandes particulieres a chaque effet */
	else
	{
		/* Si la commande est associee a l'effet courant, on la
		   prend. Sinon, on l'ignore. */
		if (command == track_info.mix.fx.effect)
		{
			assert (command < FxPreset_NBR_TYPES);
			const TrackFncPtr	fnc_ptr =
				_fx_effect_command_ptr [command] [sub_command] [1];
			if (fnc_ptr != NULL)
			{
				track_info.score.score.fx_par = parameter;
				(this->*fnc_ptr) (track_info);
			}
		}
	}

	/* Met a jour la taille des buffers */
	if (track_info.mix.fx.update_flag)
	{
		fx_fx_update (track_info);
		track_info.mix.fx.update_flag = false;
	}

	if (track_info.mix.fx.bad_init_flag)
	{
		fx_fx_init (track_info);
	}
}



/****************************************************************************/
/*                                                                          */
/*      COMMANDES GENERALES, PREMIER TICK                                   */
/*                                                                          */
/****************************************************************************/



void	Player::fx_general_add_track (TrackInfo &track_info)
{
	int	track;
	int		type;
	int		track_type;
	PLAY_SOURCE_TRACK_CONF_BLOCK	track_conf;

	track = (int) (track_info.score.score.fx_par & 0xFF) - 1;
	type = (int) (track_info.score.score.fx_par >> 8);
	if (type < 0 || type >= 4)
	{
		return;
	}

	track_type = _fx_track_type_list [type].track_type;
	if (   track >= GTK_nbr_tracks [track_type]
	    || track < 0)
	{
		return;
	}

	track_conf.track_type = track_type;
	track_conf.track_nbr = track;
	track_conf.wet_flag = _fx_track_type_list [type].wet_flag;
	track_conf.inpvol = 0x1000;
	track_conf.inppan = 0x8000U;
	add_source_track (Pattern_TYPE_FX, track_info.track,
	                       &track_conf, Player::MIX_PRESET_REPL);
}



void	Player::fx_general_remove_track (TrackInfo &track_info)
{
	int	track;
	int		type;
	int		track_type;
	PLAY_SOURCE_TRACK_CONF_BLOCK	track_conf;

	track = (int) (track_info.score.score.fx_par & 0xFF) - 1;
	type = (int) (track_info.score.score.fx_par >> 8);
	if (type < 0 || type >= 4)
	{
		return;
	}

	track_type = _fx_track_type_list [type].track_type;
	if (   track >= GTK_nbr_tracks [track_type]
	    || track < 0)
	{
		return;
	}

	track_conf.track_type = track_type;
	track_conf.track_nbr = track;
	track_conf.wet_flag = _fx_track_type_list [type].wet_flag;
	remove_source_track (Pattern_TYPE_FX, track_info.track, &track_conf);
}



void	Player::fx_general_apply_preset (TrackInfo &track_info)
{
	LWORD		param;

	param = track_info.score.score.fx_par;
	if (   param > 0
	    && param <= FXP_NBRFXP_MAXI)
	{
		Player::fx_fx_set_preset (track_info, FXP_get_parameters (param),
		                      FXP_get_effect_type (param), 0xFFFFFFFFL);
	}
}



void	Player::fx_general_remove_fx (TrackInfo &track_info)
{
	fx_fx_restore (track_info);
}



void	Player::fx_general_mute_input_tracks (TrackInfo &track_info)
{
	int		src_cnt;

	for (src_cnt = 0; src_cnt < track_info.mix.src.nbr_s_tracks; src_cnt ++)
	{
		track_info.mix.src.track_conf [src_cnt].inpvol = 0;
	}
}



void	Player::fx_general_set_stereo (TrackInfo &track_info)
{
	LWORD		param;

	param = track_info.score.score.fx_par;
	if (param > 0)
	{
		fx_fx_set_stereo (track_info, (int) (param & 0xF));
	}
}



/****************************************************************************/
/*                                                                          */
/*      COMMANDES GENERALES, TOUS LES TICKS                                 */
/*                                                                          */
/****************************************************************************/



/****************************************************************************/
/*                                                                          */
/*      DELAY                                                               */
/*                                                                          */
/****************************************************************************/



void	Player::fx_delay_set_default_parameters (TrackInfo &track_info)
{
	FxPreset::reset_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DELAY);

	track_info.mix.fx.update_flag = true;
}



int	Player::fx_delay_init (TrackInfo &track_info)
{
	float *	buf_ptr;
	LWORD		delay;
	LWORD		buf_len;

	track_info.stereo = 1;

	delay = (LWORD) (track_info.mix.fx.conf.delay.delay * MIX_replay_freq);
	buf_len = (delay + _fx_delay_buffer_granul - 1) & ~(_fx_delay_buffer_granul - 1);
	buf_ptr = (float *) MALLOC (buf_len * sizeof (*buf_ptr));
	if (buf_ptr == 0)
	{
		track_info.mix.fx.bad_init_flag = true;
		LOG_printf ("fx_delay_init: warning: buffer creation failed.\n");
		return (-1);
	}
	memset (buf_ptr, 0, buf_len * sizeof (*buf_ptr));

	track_info.mix.fx.internal.delay.buf_ptr = buf_ptr;
	track_info.mix.fx.internal.delay.buf_len = buf_len;
	track_info.mix.fx.internal.delay.delay = delay;
	track_info.mix.fx.internal.delay.write_pos = 0;
	track_info.mix.fx.internal.delay.feedback =
		(ULWORD) (track_info.mix.fx.conf.delay.feedback * 0xFFFFFFFFUL);

	track_info.mix.fx.effect = FxPreset_TYPE_DELAY;
	track_info.mix.fx.bad_init_flag = false;
	track_info.mix.fx.update_flag = false;

	return (0);
}



int	Player::fx_delay_set_stereo (TrackInfo &track_info, int stereo)
{
	track_info.stereo = 1;

	return (track_info.stereo);
}



int	Player::fx_delay_silence (TrackInfo &track_info)
{
	memset (track_info.mix.fx.internal.delay.buf_ptr, 0,
	          track_info.mix.fx.internal.delay.buf_len
	        * sizeof (*track_info.mix.fx.internal.delay.buf_ptr));

	return (0);
}



int	Player::fx_delay_update (TrackInfo &track_info)
{
	float *	buf_ptr;
	LWORD		delay;
	LWORD		old_buf_len;
	LWORD		buf_len;
	LWORD		write_pos;
	SLWORD	read_pos;

/*______________________________________________
 *
 * Parametres internes
 *______________________________________________
 */

	track_info.mix.fx.internal.delay.feedback =
		(ULWORD) (track_info.mix.fx.conf.delay.feedback * 0xFFFFFFFFUL);
	delay = (LWORD) (track_info.mix.fx.conf.delay.delay * MIX_replay_freq);
	delay = MAX (delay, 1);		// Toujours un sample dans le buffer
	track_info.mix.fx.internal.delay.delay = delay;

/*______________________________________________
 *
 * Taille des buffers
 *______________________________________________
 */

	old_buf_len = track_info.mix.fx.internal.delay.buf_len;
	buf_len = (delay + _fx_delay_buffer_granul - 1) & ~(_fx_delay_buffer_granul - 1);

	/* On change la taille du buffer */
	if (buf_len != old_buf_len)
	{
		buf_ptr = track_info.mix.fx.internal.delay.buf_ptr;

		/* Reduction de la taille du buffer. On essaie de conserver les donnees
		   entrees le plus recemment dans le buffer. */
		if (buf_len < old_buf_len)
		{
			write_pos = track_info.mix.fx.internal.delay.write_pos;
			read_pos = write_pos - delay;

			/* 1er cas: Les positions R et W sont hors du nouveau buffer */
			if (read_pos >= buf_len)
			{
				memmove (buf_ptr,
				         buf_ptr + write_pos - buf_len,
				         buf_len * sizeof (*buf_ptr));
				write_pos = 0;
			}

			/* 2eme cas: R dans le buffer, W hors du buffer */
			else if (   read_pos < buf_len
			         && write_pos >= buf_len)
			{
				memmove (buf_ptr,
				         buf_ptr + buf_len,
				         (write_pos - buf_len) * sizeof (*buf_ptr));
				write_pos -= buf_len;
			}

			/* 3eme cas: R et W sont dans le buffer */
			/* 4eme cas: W est dans le buffer, R hors du buffer */
			else if (write_pos < buf_len)
			{
				memmove (buf_ptr + write_pos,
				         buf_ptr + old_buf_len - buf_len + write_pos,
				         (buf_len - write_pos) * sizeof (*buf_ptr));
			}

			if (write_pos > buf_len)
			{
				write_pos -= buf_len;
			}
			track_info.mix.fx.internal.delay.write_pos = write_pos;
		}

		buf_ptr = (float *) REALLOC (buf_ptr, buf_len * sizeof (*buf_ptr));
		if (buf_ptr == 0)
		{
			LOG_printf ("fx_delay_update: warning: couldn't change buffer length.\n");
			FREE (track_info.mix.fx.internal.delay.buf_ptr);
			track_info.mix.fx.bad_init_flag = true;
			return (-1);
		}

		/* Agrandissement */
		if (buf_len > old_buf_len)
		{
			write_pos = track_info.mix.fx.internal.delay.write_pos;

			memmove (buf_ptr + buf_len - old_buf_len + write_pos,
			         buf_ptr + write_pos,
			         (old_buf_len - write_pos) * sizeof (*buf_ptr));
			memset (buf_ptr + write_pos, 0, (buf_len - old_buf_len) * sizeof (*buf_ptr));
		}

		track_info.mix.fx.internal.delay.buf_ptr = buf_ptr;
		track_info.mix.fx.internal.delay.buf_len = buf_len;
	}

	return (0);
}



void	Player::fx_delay_restore (TrackInfo &track_info)
{
	FREE (track_info.mix.fx.internal.delay.buf_ptr);
}



/****************************************************************************/
/*                                                                          */
/*      DELAY, PREMIER TICK                                                 */
/*                                                                          */
/****************************************************************************/



void	Player::fx_delay_activate (TrackInfo &track_info)
{
	/* Si on etait deja en delay, on peut tout de suite se barrer */
	if (track_info.mix.fx.old_effect == FxPreset_TYPE_DELAY)
	{
		return;
	}

	/* Sinon, il faut penser a virer l'ancien */
	else
	{
		fx_fx_restore (track_info);
	}

	fx_delay_set_default_parameters (track_info);
	fx_delay_init (track_info);
}



void	Player::fx_delay_modify_time_1st_tick (TrackInfo &track_info)
{
	double	val;

	val = track_info.mix.fx.conf.delay.delay;
	if (fx_param_to_time_1st_tick (track_info,
	                                   track_info.score.score.fx_data, val,
	                                   track_info.score.fx.time [0].speed,
	                                   track_info.score.fx.time [0].time))
	{
		fx_delay_do_set_time (track_info, val);
	}
}



void	Player::fx_delay_modify_feedback_1st_tick (TrackInfo &track_info)
{
	double	val;
	double	dummy;

	dummy = 0;
	val = track_info.mix.fx.conf.delay.feedback;
	if (fx_param_to_double_1st_tick (track_info.score.score.fx_data, val, dummy, 0.0001, false))
	{
		fx_delay_do_set_feedback (track_info, val);
	}
}



/****************************************************************************/
/*                                                                          */
/*      DELAY, TOUS LES TICKS                                               */
/*                                                                          */
/****************************************************************************/



void	Player::fx_delay_modify_time_all_ticks (TrackInfo &track_info)
{
	double	val;

	val = track_info.mix.fx.conf.delay.delay;
	if (fx_param_to_time_all_ticks (track_info,
	                                    track_info.score.score.fx_data, val,
	                                    track_info.score.fx.time [0].speed,
	                                    track_info.score.fx.time [0].time))
	{
		fx_delay_do_set_time (track_info, val);
	}
}



void	Player::fx_delay_modify_feedback_all_ticks (TrackInfo &track_info)
{
	double	val;
	double	dummy;

	dummy = 0;
	val = track_info.mix.fx.conf.delay.feedback;
	if (fx_param_to_double_all_ticks (track_info.score.score.fx_data, val, dummy, 0.0001))
	{
		fx_delay_do_set_feedback (track_info, val);
	}
}



/****************************************************************************/
/*                                                                          */
/*      DELAY, FONCTIONS COMMUNES                                           */
/*                                                                          */
/****************************************************************************/



void	Player::fx_delay_do_set_time (TrackInfo &track_info, double delay_time)
{
	delay_time = MIN (delay_time, FxPreset_DELAY_MAX_TIME);
	delay_time = MAX (delay_time, FxPreset_DELAY_MIN_TIME);
	track_info.mix.fx.conf.delay.delay = delay_time;
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_delay_do_set_feedback (TrackInfo &track_info, double feedback)
{
	feedback = MIN (feedback, FxPreset_DELAY_MAX_FDBK);
	feedback = MAX (feedback, FxPreset_DELAY_MIN_FDBK);
	track_info.mix.fx.conf.delay.feedback = feedback;
	track_info.mix.fx.update_flag = true;
}



/****************************************************************************/
/*                                                                          */
/*      FILTRE RESONNANT                                                    */
/*                                                                          */
/****************************************************************************/



void	Player::fx_resfilt_set_default_parameters (TrackInfo &track_info)
{
	FxPreset::reset_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);

	track_info.mix.fx.update_flag = true;
}



int	Player::fx_resfilt_init (TrackInfo &track_info)
{
	int		biq_cnt;

	for (biq_cnt = 0; biq_cnt < FxPreset_RESFILT_MAX_BIQUAD; biq_cnt ++)
	{
		track_info.mix.fx.internal.resfilt.biquad [biq_cnt].buffer_pos = 0;
	}

	track_info.mix.fx.effect = FxPreset_TYPE_RESFILT;
	track_info.mix.fx.bad_init_flag = false;
	track_info.mix.fx.update_flag = true;

	return (0);
}



int	Player::fx_resfilt_set_stereo (TrackInfo &track_info, int stereo)
{
	int		chn_cnt;
	int		spl_cnt;
	int		biq_cnt;
	int		nbr_biquads;
	float		val_x;
	double	val_y;

	if (track_info.stereo != stereo)
	{
		nbr_biquads = track_info.mix.fx.conf.resfilt.nbr_biquads;
		if (track_info.mix.fx.conf.resfilt.cell_flag)
		{
			nbr_biquads *= track_info.mix.fx.conf.resfilt.nbr_cells + 1;
		}

		if (! track_info.mix.fx.bad_init_flag)
		{
			/* Mono -> stereo */
			if (track_info.stereo == 1)
			{
				for (biq_cnt = 0; biq_cnt < nbr_biquads; biq_cnt ++)
				{
					for (spl_cnt = 0; spl_cnt < Player::FX_RESFILT_INT_BUF_LEN; spl_cnt ++)
					{
						val_x = track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_buffer [spl_cnt] / stereo;
						val_y = track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_buffer [spl_cnt] / stereo;
						for (chn_cnt = 0; chn_cnt < stereo; chn_cnt ++)
						{
							track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_buffer
								[chn_cnt * Player::FX_RESFILT_INT_BUF_LEN + spl_cnt] = val_x;
							track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_buffer
								[chn_cnt * Player::FX_RESFILT_INT_BUF_LEN + spl_cnt] = val_y;
						}
					}
				}
			}

			/* Stereo -> mono */
			else
			{
				for (biq_cnt = 0; biq_cnt < nbr_biquads; biq_cnt ++)
				{
					for (spl_cnt = 0; spl_cnt < Player::FX_RESFILT_INT_BUF_LEN; spl_cnt ++)
					{
						val_x = 0;
						val_y = 0;
						for (chn_cnt = 0; chn_cnt < track_info.stereo; chn_cnt ++)
						{
							val_x += track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_buffer
								[chn_cnt * Player::FX_RESFILT_INT_BUF_LEN + spl_cnt];
							val_y += track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_buffer
								[chn_cnt * Player::FX_RESFILT_INT_BUF_LEN + spl_cnt];
						}
						track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_buffer [spl_cnt] = val_x;
						track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_buffer [spl_cnt] = val_y;
					}
				}
			}

			track_info.mix.fx.update_flag = true;
		}

		track_info.stereo = stereo;
	}

	return (track_info.stereo);
}



int	Player::fx_resfilt_silence (TrackInfo &track_info)
{
	int		biq_cnt;
	int		nbr_biquads;

	nbr_biquads = track_info.mix.fx.conf.resfilt.nbr_biquads;
	if (track_info.mix.fx.conf.resfilt.cell_flag)
	{
		nbr_biquads *= track_info.mix.fx.conf.resfilt.nbr_cells + 1;
	}

	/* Vide les buffers des filtres */
	for (biq_cnt = 0; biq_cnt < nbr_biquads; biq_cnt ++)
	{
		memset (track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_buffer, 0,
		          Player::FX_RESFILT_INT_BUF_LEN * 2
		        * sizeof (*track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_buffer));
		memset (track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_buffer, 0,
		          Player::FX_RESFILT_INT_BUF_LEN * 2
		        * sizeof (*track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_buffer));
		track_info.mix.fx.internal.resfilt.biquad [biq_cnt].buffer_pos = 0;
	}

	return (0);
}



int	Player::fx_resfilt_update (TrackInfo &track_info)
{
	double	main_freq;
	double	cell_freq;
	double	freq_interval;
	double	real_q;
	long		wave_pos;
	int		biq_cnt;
	int		cell_cnt;
	int		type;
	int		order_cnt;

	/* Calcule la frequence centrale courante */
	main_freq = track_info.mix.fx.conf.resfilt.freq;
	if (track_info.mix.fx.conf.resfilt.lfo_flag)
	{
		wave_pos = (long) (  track_info.mix.fx.conf.resfilt.lfo_phase
		                   * WaveForm_BASE_LENGTH);
		main_freq *= exp (  WaveForm::base_ptr [track_info.mix.fx.conf.resfilt.lfo_waveform]->get_point_fast (wave_pos)
		                  * track_info.mix.fx.conf.resfilt.lfo_depth
		                  * (LN2 / 12));
	}

	/* Calcule le Q reelement utilise si l'ordre des cellules est > 2 */
	if (track_info.mix.fx.conf.resfilt.nbr_biquads > 1)
	{
		real_q = pow (track_info.mix.fx.conf.resfilt.q,
		              1.0 / track_info.mix.fx.conf.resfilt.nbr_biquads);
	}
	else
	{
		real_q = track_info.mix.fx.conf.resfilt.q;
	}

	/* Fabrique la premiere serie de biquads */
	track_info.mix.fx.internal.resfilt.mix_cell_flag = false;
	biq_cnt = 0;
	for (order_cnt = 0; order_cnt < track_info.mix.fx.conf.resfilt.nbr_biquads; order_cnt ++)
	{
		FILT_init_filter (track_info.mix.fx.internal.resfilt.l_num,
		                  track_info.mix.fx.internal.resfilt.l_den,
		                  track_info.mix.fx.internal.resfilt.biquad [biq_cnt].nbr_coef);

		FILT_add_biquad (track_info.mix.fx.internal.resfilt.l_num,
		                 track_info.mix.fx.internal.resfilt.l_den,
		                 track_info.mix.fx.internal.resfilt.biquad [biq_cnt].nbr_coef,
		                 MIX_replay_freq, main_freq, main_freq, real_q,
		                 track_info.mix.fx.conf.resfilt.type, false);

		FILT_laplace_to_z (track_info.mix.fx.internal.resfilt.l_num,
		                   track_info.mix.fx.internal.resfilt.l_den,
								 track_info.mix.fx.internal.resfilt.biquad [biq_cnt].nbr_coef,
		                   MIX_replay_freq, main_freq,
		                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_coef,
		                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_coef);

		FILT_z_to_diff_eq (track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_coef,
		                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_coef,
								 track_info.mix.fx.internal.resfilt.biquad [biq_cnt].nbr_coef,
		                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_coef,
		                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_coef);

		biq_cnt ++;
	}

	/* Fabrique les autres biquads en cas de mode multibande */
	if (track_info.mix.fx.conf.resfilt.cell_flag)
	{
		freq_interval = main_freq * (pow (2.0, track_info.mix.fx.conf.resfilt.cell_ratio / 12.0) - 1.0);
		cell_freq = main_freq + freq_interval;

		/* Type de filtre utilise pour les bandes supplementaires */
		switch (track_info.mix.fx.conf.resfilt.type)
		{
		case	FILT_BIQUAD_LOWP:
			type = (freq_interval > 0) ? FILT_BIQUAD_BANDP : FILT_BIQUAD_NOTCH;
			break;

		case	FILT_BIQUAD_HIGHP:
			type = (freq_interval < 0) ? FILT_BIQUAD_BANDP : FILT_BIQUAD_NOTCH;
			break;

		case	FILT_BIQUAD_BANDP:
		case	FILT_BIQUAD_NOTCH:
		default:
			type = track_info.mix.fx.conf.resfilt.type;
			break;
		}
		track_info.mix.fx.internal.resfilt.mix_cell_flag = (type == FILT_BIQUAD_BANDP);

		/* Pour chaque bande */
		for (cell_cnt = 0; cell_cnt < track_info.mix.fx.conf.resfilt.nbr_cells; cell_cnt ++)
		{
			if (cell_freq > 1.0)		// Limite: 1 Hz
			{
				/* Pour chaque biquad */
				for (order_cnt = 0; order_cnt < track_info.mix.fx.conf.resfilt.nbr_biquads; order_cnt ++)
				{
					FILT_init_filter (track_info.mix.fx.internal.resfilt.l_num,
					                  track_info.mix.fx.internal.resfilt.l_den,
					                  track_info.mix.fx.internal.resfilt.biquad [biq_cnt].nbr_coef);

					FILT_add_biquad (track_info.mix.fx.internal.resfilt.l_num,
					                 track_info.mix.fx.internal.resfilt.l_den,
					                 track_info.mix.fx.internal.resfilt.biquad [biq_cnt].nbr_coef,
					                 MIX_replay_freq, cell_freq, cell_freq,
					                 real_q, type, false);

					FILT_laplace_to_z (track_info.mix.fx.internal.resfilt.l_num,
					                   track_info.mix.fx.internal.resfilt.l_den,
											 track_info.mix.fx.internal.resfilt.biquad [biq_cnt].nbr_coef,
					                   MIX_replay_freq, cell_freq,
					                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_coef,
					                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_coef);

					FILT_z_to_diff_eq (track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_coef,
					                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_coef,
											 track_info.mix.fx.internal.resfilt.biquad [biq_cnt].nbr_coef,
					                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].x_coef,
					                   track_info.mix.fx.internal.resfilt.biquad [biq_cnt].y_coef);

					biq_cnt ++;
				}
			}

			cell_freq += freq_interval;
		}
	}

	return (0);
}



void	Player::fx_resfilt_restore (TrackInfo &track_info)
{
	/* Rien a faire */
}



/****************************************************************************/
/*                                                                          */
/*      FILTRE RESONNANT, PREMIER TICK                                      */
/*                                                                          */
/****************************************************************************/



void	Player::fx_resfilt_activate (TrackInfo &track_info)
{
	int		val;
	int		old_order;
	int		new_order;

	/* Si on n'etait pas en filtre, il faut penser a virer l'ancien effet */
	if (track_info.mix.fx.old_effect != FxPreset_TYPE_RESFILT)
	{
		fx_fx_restore (track_info);
		fx_resfilt_set_default_parameters (track_info);
	}

	/* Memorise l'ordre */
	old_order = track_info.mix.fx.conf.resfilt.nbr_biquads * 2;
	if (track_info.mix.fx.conf.resfilt.cell_flag)
	{
		old_order *= track_info.mix.fx.conf.resfilt.nbr_cells + 1;
	}

	/* Fixe le type de filtre */
	val = (int)track_info.score.score.fx_par & 0xF;
	if (val > 0)
	{
		track_info.mix.fx.conf.resfilt.type = val - 1;
	}

	/* Fixe l'ordre du filtre */
	val = (int) (track_info.score.score.fx_par >> 4) & 0xF;
	if (val > 0)
	{
		track_info.mix.fx.conf.resfilt.nbr_biquads = val;
	}

	/* Active le LFO */
	val = (int) (track_info.score.score.fx_par >> 8) & 0xF;
	if (val > 0)
	{
		track_info.mix.fx.conf.resfilt.lfo_flag = (val != 0xF);
	}

	/* Active le multiband mode */
	val = (int) (track_info.score.score.fx_par >> 12) & 0xF;
	if (val > 0)
	{
		track_info.mix.fx.conf.resfilt.cell_flag = (val != 0xF);
		track_info.mix.fx.conf.resfilt.nbr_cells = val;
	}

	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);

	/* Calcule le nouvel ordre, et vide les buffers si on a augmente */
	new_order = track_info.mix.fx.conf.resfilt.nbr_biquads * 2;
	if (track_info.mix.fx.conf.resfilt.cell_flag)
	{
		new_order *= track_info.mix.fx.conf.resfilt.nbr_cells + 1;
	}
	if (new_order > old_order)
	{
		fx_resfilt_silence (track_info);
	}

	/* Si on etait deja en filtre resonnant, on peut se barrer maintenant. */
	if (track_info.mix.fx.old_effect == FxPreset_TYPE_RESFILT)
	{
		track_info.mix.fx.update_flag = true;
		return;
	}

	fx_resfilt_init (track_info);
}



void	Player::fx_resfilt_modify_freq_1st_tick (TrackInfo &track_info)
{
	track_info.mix.fx.update_flag |=
		fx_param_to_freq_1st_tick (track_info,
		                               track_info.score.score.fx_data,
		                               track_info.mix.fx.conf.resfilt.freq,
		                               track_info.score.fx.freq [0].speed,
		                               track_info.score.fx.freq [0].freq);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
}



void	Player::fx_resfilt_modify_q_1st_tick (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_1st_tick (track_info.score.score.fx_data,
	                                 track_info.mix.fx.conf.resfilt.q,
	                                 dummy, 0.01, false);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_resfilt_modify_nbr_biq_1st_tick (TrackInfo &track_info)
{
	int		dummy;

	dummy = 0;
	fx_param_to_int_1st_tick (track_info.score.score.fx_data,
	                              track_info.mix.fx.conf.resfilt.nbr_biquads,
	                              dummy, 1);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_resfilt_modify_lfo_freq_1st_tick (TrackInfo &track_info)
{
	track_info.mix.fx.update_flag |= 
		fx_param_to_freq_1st_tick (track_info,
		                               track_info.score.score.fx_data,
		                               track_info.mix.fx.conf.resfilt.lfo_freq,
		                               track_info.score.fx.freq [1].speed,
		                               track_info.score.fx.freq [1].freq);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
}



void	Player::fx_resfilt_modify_lfo_depth_1st_tick (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_1st_tick (track_info.score.score.fx_data,
	                                 track_info.mix.fx.conf.resfilt.lfo_depth,
	                                 dummy, 0.001, false);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_resfilt_set_lfo_waveform (TrackInfo &track_info)
{
	int		param;

	param = (int) (track_info.score.score.fx_par & 0xF);
	if (param > 0)
	{
		track_info.mix.fx.conf.resfilt.lfo_waveform = param - 1;
	}
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_resfilt_set_lfo_phase (TrackInfo &track_info)
{
	LWORD		param;

	param = track_info.score.score.fx_par;
	track_info.mix.fx.conf.resfilt.lfo_phase = (double)param / 36000;
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_resfilt_modify_cell_ratio_1st_tick (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_1st_tick (track_info.score.score.fx_data,
	                                 track_info.mix.fx.conf.resfilt.cell_ratio,
	                                 dummy, 0.001, false);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



/****************************************************************************/
/*                                                                          */
/*      FILTRE RESONNANT, TOUS LES TICKS                                    */
/*                                                                          */
/****************************************************************************/



void	Player::fx_resfilt_modify_freq_all_ticks (TrackInfo &track_info)
{
	track_info.mix.fx.update_flag |=
		fx_param_to_freq_all_ticks (track_info,
		                                track_info.score.score.fx_data,
		                                track_info.mix.fx.conf.resfilt.freq,
		                                track_info.score.fx.freq [0].speed,
		                                track_info.score.fx.freq [0].freq);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
}



void	Player::fx_resfilt_modify_q_all_ticks (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_all_ticks (track_info.score.score.fx_data,
	                                  track_info.mix.fx.conf.resfilt.q,
	                                  dummy, 0.01);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_resfilt_modify_nbr_biq_all_ticks (TrackInfo &track_info)
{
	int		dummy;

	dummy = 0;
	fx_param_to_int_all_ticks (track_info.score.score.fx_data,
	                               track_info.mix.fx.conf.resfilt.nbr_biquads,
	                               dummy, 1);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_resfilt_modify_lfo_freq_all_ticks (TrackInfo &track_info)
{
	track_info.mix.fx.update_flag |=
		fx_param_to_freq_all_ticks (track_info,
		                                track_info.score.score.fx_data,
		                                track_info.mix.fx.conf.resfilt.lfo_freq,
		                                track_info.score.fx.freq [1].speed,
		                                track_info.score.fx.freq [1].freq);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
}



void	Player::fx_resfilt_modify_lfo_depth_all_ticks (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_all_ticks (track_info.score.score.fx_data,
	                                  track_info.mix.fx.conf.resfilt.lfo_depth,
	                                  dummy, 0.001);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_RESFILT);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_resfilt_modify_cell_ratio_all_ticks (TrackInfo &track_info)
{
}



/****************************************************************************/
/*                                                                          */
/*      DISTORSION                                                          */
/*                                                                          */
/****************************************************************************/



void	Player::fx_disto_set_default_parameters (TrackInfo &track_info)
{
	FxPreset::reset_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DISTO);

	track_info.mix.fx.update_flag = true;
}



int	Player::fx_disto_init (TrackInfo &track_info)
{
	track_info.mix.fx.effect = FxPreset_TYPE_DISTO;
	track_info.mix.fx.bad_init_flag = false;
	track_info.mix.fx.update_flag = true;

	return (0);
}



int	Player::fx_disto_set_stereo (TrackInfo &track_info, int stereo)
{
	if (track_info.stereo != stereo)
	{
		if (! track_info.mix.fx.bad_init_flag)
		{
//			track_info.mix.fx.update_flag = true;
		}

		track_info.stereo = stereo;
	}

	return (track_info.stereo);
}



int	Player::fx_disto_silence (TrackInfo &track_info)
{
	/* Rien */

	return (0);
}



int	Player::fx_disto_update (TrackInfo &track_info)
{
	double	gain;
	double	k;
	double	g;
	double	threshold;
	double	p;
	int		it_cnt;

	gain = BASE_db_to_lin (track_info.mix.fx.conf.disto.gain);

	switch (track_info.mix.fx.conf.disto.type)
	{

/*______________________________________________
 *
 * Mode Boost (log)
 *______________________________________________
 */

	case	FxPreset_DISTO_TYPE_BOOST:

		/* Caclule le gain interne et le gain post-distorsion de facon a avoir
		   une pente a l'orignie correspondant au gain demande et une sortie
			normalisee. On procede par approximations successives. */
		g = gain;
		for (it_cnt = 0; it_cnt < 5; it_cnt ++)
		{
			/* Calcule le correctif pour obtenir une sortie
				de 1 avec une entree de 1. */
			k = 1.0 / log (1.0 + log (1.0 + g));

			/* On a cependant modifie le gain final a l'origine, donc
				on le modifie de facon a obtenir le gain demande. */
			g = gain / k;
		}

		/* On reevalue k une derniere fois car c'est le critere le
			plus important. */
		k = 1.0 / log (1.0 + log (1.0 + g));

		track_info.mix.fx.internal.disto.g = g * (1.0/0x800000);	// 24 bits -> normalise
		track_info.mix.fx.internal.disto.k = k * 0x800000;	// Normalise -> 24 bits
		break;

/*______________________________________________
 *
 * Mode Punch (sin)
 *______________________________________________
 */

	case	FxPreset_DISTO_TYPE_PUNCH:

		threshold = BASE_db_to_lin (track_info.mix.fx.conf.disto.threshold);
		p = gain / BASE_db_to_lin (track_info.mix.fx.conf.disto.sec_gain);
		g = gain - p;

		/* Caclule le gain interne et le gain post-distorsion de facon a avoir
		   une pente a l'orignie correspondant au gain demande et une sortie
			normalisee. On procede par approximations successives. */
		if (g < PI * 0.5)
		{
			for (it_cnt = 0; it_cnt < 5; it_cnt ++)
			{
		      k = 1.0 / sin (g);
				g = (gain - p) / k;
			}
	      k = 1.0 / sin (g);
		}

		/* Recalcule le gain secondaire et le gain interne en fonction
		   du gain reel demande. */
		else
		{
			k = 1.0;
			if (g > threshold)
			{
				/* Sorte de moyenne entre g et l, qui colle a g
					juste apres l et qui finit par plafonner. */
		      g = threshold * (1.0 + log (g / threshold));
				p = gain - g;
			}
		}

		track_info.mix.fx.internal.disto.g = g * (1.0/0x800000);	// 24 bits -> normalise
		track_info.mix.fx.internal.disto.k = k * 0x800000;	// Normalise -> 24 bits
		track_info.mix.fx.internal.disto.p = p;
		break;
	}

	return (0);
}



void	Player::fx_disto_restore (TrackInfo &track_info)
{
	/* Rien */
}






/****************************************************************************/
/*                                                                          */
/*      DISTORSION, PREMIER TICK                                            */
/*                                                                          */
/****************************************************************************/



void	Player::fx_disto_activate (TrackInfo &track_info)
{
	int		val;

	/* Si on n'etait pas en disto, il faut penser a virer l'ancien effet */
	if (track_info.mix.fx.old_effect != FxPreset_TYPE_DISTO)
	{
		fx_fx_restore (track_info);
		fx_disto_set_default_parameters (track_info);
	}

	/* Recupere les parametres */
	val = (int)track_info.score.score.fx_par & 0xF;
	if (val > 0)
	{
		track_info.mix.fx.conf.disto.type = val - 1;
	}

	/* Valide les parametres modifies */
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DISTO);

	/* Si on etait deja en disto, on peut se barrer maintenant. */
	if (track_info.mix.fx.old_effect == FxPreset_TYPE_DISTO)
	{
		track_info.mix.fx.update_flag = true;
		return;
	}

	fx_disto_init (track_info);
}



void	Player::fx_disto_modify_gain_1st_tick (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_1st_tick (track_info.score.score.fx_data,
	                                 track_info.mix.fx.conf.disto.gain,
	                                 dummy, 0.001, true);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DISTO);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_disto_modify_sec_gain_1st_tick (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_1st_tick (track_info.score.score.fx_data,
	                                 track_info.mix.fx.conf.disto.sec_gain,
	                                 dummy, 0.001, false);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DISTO);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_disto_modify_threshold_1st_tick (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_1st_tick (track_info.score.score.fx_data,
	                                 track_info.mix.fx.conf.disto.threshold,
	                                 dummy, 0.001, false);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DISTO);
	track_info.mix.fx.update_flag = true;
}



/****************************************************************************/
/*                                                                          */
/*      DISTORSION, TOUS LES TICKS                                          */
/*                                                                          */
/****************************************************************************/



void	Player::fx_disto_modify_gain_all_ticks (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_all_ticks (track_info.score.score.fx_data,
	                                  track_info.mix.fx.conf.disto.gain,
	                                  dummy, 0.001);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DISTO);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_disto_modify_sec_gain_all_ticks (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_all_ticks (track_info.score.score.fx_data,
	                                  track_info.mix.fx.conf.disto.sec_gain,
	                                  dummy, 0.001);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DISTO);
	track_info.mix.fx.update_flag = true;
}



void	Player::fx_disto_modify_threshold_all_ticks (TrackInfo &track_info)
{
	double	dummy;

	dummy = 0;
	fx_param_to_double_all_ticks (track_info.score.score.fx_data,
	                                  track_info.mix.fx.conf.disto.threshold,
	                                  dummy, 0.001);
	FxPreset::validate_parameters (track_info.mix.fx.conf, FxPreset_TYPE_DISTO);
	track_info.mix.fx.update_flag = true;
}



/****************************************************************************/
/*                                                                          */
/*      DIVERS                                                              */
/*                                                                          */
/****************************************************************************/



bool	Player::fx_param_to_double_1st_tick (LWORD param, double &val, double &inc, double scale, bool zero_flag)
{
	int		effect;
	double	old_val;

	old_val = val;
	effect = (int) (param >> 16) & 0xF;
	param = param & 0xFFFFL;

	switch (effect)
	{
	/* Set */
	case	0x0:
		if (param > 0 || zero_flag)
		{
			val = (double)param * scale;
		}
		break;

	/* Fine increase */
	case	0x3:
		if (param > 0)
		{
			inc = (double)param * scale;
		}
		val += inc;
		break;

	/* Fine decrease */
	case	0x4:
		if (param > 0)
		{
			inc = (double)param * scale;
		}
		val -= inc;
		break;
	}

	return (val != old_val);
}



bool	Player::fx_param_to_double_all_ticks (LWORD param, double &val, double &inc, double scale)
{
	int		effect;
	double	old_val;

	old_val = val;
	effect = (int) (param >> 16) & 0xF;
	param = param & 0xFFFFL;

	switch (effect)
	{
	/* Increase */
	case	0x1:
		if (param > 0)
		{
			inc = (double)param * scale;
		}
		val += inc;
		break;

	/* Decrease */
	case	0x2:
		if (param > 0)
		{
			inc = (double)param * scale;
		}
		val -= inc;
		break;
	}

	return (val != old_val);
}



bool	Player::fx_param_to_int_1st_tick (LWORD param, int &val, int &inc, int scale)
{
	int		effect;
	int		old_val;

	old_val = val;
	effect = (int) (param >> 16) & 0xF;
	param = param & 0xFFFFL;

	switch (effect)
	{
	/* Set */
	case	0x0:
		if (param > 0)
		{
			val = param * scale;
		}
		break;

	/* Fine increase */
	case	0x3:
		if (param > 0)
		{
			inc = param * scale;
		}
		val += inc;
		break;

	/* Fine decrease */
	case	0x4:
		if (param > 0)
		{
			inc = param * scale;
		}
		val -= inc;
		break;
	}

	return (val != old_val);
}



bool	Player::fx_param_to_int_all_ticks (LWORD param, int &val, int &inc, int scale)
{
	int		effect;
	int		old_val;

	old_val = val;
	effect = (int) (param >> 16) & 0xF;
	param = param & 0xFFFFL;

	switch (effect)
	{
	/* Increase */
	case	0x1:
		if (param > 0)
		{
			inc = param * scale;
		}
		val += inc;
		break;

	/* Decrease */
	case	0x2:
		if (param > 0)
		{
			inc = param * scale;
		}
		val -= inc;
		break;
	}

	return (val != old_val);
}



bool	Player::fx_param_to_time_1st_tick (TrackInfo &track_info, LWORD param, double &val, double &inc, double &to_reach)
{
	int		note;
	int		effect;
	int		speed;
	double	old_val;

	old_val = val;
	effect = (int) (param >> 16) & 0xF;
	param = param & 0xFFFFL;

	switch (effect)
	{
	/* ms */
	case	0x0:
		if (param > 0)
		{
			val = (double)param / 100000;
		}
		break;

	/* Fine time increase (ms) */
	case	0x3:
		if (param > 0)
		{
			inc = (double)param / 100000;
		}
		val += inc;
		break;

	/* Fine time decrease (ms) */
	case	0x4:
		if (param > 0)
		{
			inc = (double)param / 100000;
		}
		val -= inc;
		break;

	/* Partition */
	case	0x7:
		track_info.score.score.note = (param >> 8) & 0xFF;
		track_info.score.score.instr = param & 0xFF;
		
		/* On ne gere pas ici la nouvelle note si l'effet est un Note delay */
		if ((track_info.score.score.effect >> 8) != 0x09)
		{
			set_new_note (track_info);
		}
		break;

	/* s */
	case	0x8:
		if (param > 0)
		{
			val = (double)param / 1000;
		}
		break;

	/* Fine time increase (s) */
	case	0xB:
		if (param > 0)
		{
			inc = (double)param / 1000;
		}
		val += inc;
		break;

	/* Fine time decrease (s) */
	case	0xC:
		if (param > 0)
		{
			inc = (double)param / 1000;
		}
		val -= inc;
		break;

	/* Note + finetune */
	case	0xD:
		if (param > 0)
		{
			val = 1.0 / convert_note_to_freq ((param >> 8) & 0xFF,
			                                       (double) (param & 0xFF) / 256);
		}
		break;

	/* Note slide */
	case	0xE:
		note = (param >> 8) & 0xFF;
		speed = param & 0xFF;

		if (note != 0)
		{
			to_reach = 1.0 / convert_note_to_freq ((int) note, 0);
		}
		if (speed != 0)
		{
			inc = speed << 4;
		}
		break;

	/* Beat */
	case	0xF:
		fx_beat_to_time (param, val);
		break;
	}

	return (val != old_val);
}



bool	Player::fx_param_to_time_all_ticks (TrackInfo &track_info, LWORD param, double &val, double &inc, double to_reach)
{
	int		effect;
	double	old_val;

	old_val = val;
	effect = (int) (param >> 16) & 0xF;
	param = param & 0xFFFFL;

	switch (effect)
	{
	/* Time increase (ms) */
	case	0x1:
		if (param > 0)
		{
			inc = (double)param / 100000;
		}
		val += inc;
		break;

	/* Time decrease (ms) */
	case	0x2:
		if (param > 0)
		{
			inc = (double)param / 100000;
		}
		val -= inc;
		break;

	/* Partition */
	case	0x7:
		val = convert_period_to_time (track_info.score.period_loc);
		break;

	/* Time increase (s) */
	case	0x9:
		if (param > 0)
		{
			inc = (double)param / 1000;
		}
		val += inc;
		break;

	/* Time decrease (s) */
	case	0xA:
		if (param > 0)
		{
			inc = (double)param / 1000;
		}
		val -= inc;
		break;

	/* Note slide */
	case	0xE:
		if (to_reach > val)
		{
			val += inc;
			val = MIN (val, to_reach);
		}
		else
		{
			val -= inc;
			val = MAX (val, to_reach);
		}
		break;

	}

	return (val != old_val);
}



bool	Player::fx_param_to_freq_1st_tick (TrackInfo &track_info, LWORD param, double &val, double &inc, double &to_reach)
{
	int		note;
	int		effect;
	int		speed;
	double	old_val;

	old_val = val;
	effect = (int) (param >> 16) & 0xF;
	param = param & 0xFFFFL;

	switch (effect)
	{
	/* Hz */
	case	0x0:
		if (param > 0)
		{
			val = (double)param;
		}
		break;

	/* Fine freq increase (10000 donne freq * 2) */
	case	0x3:
		if (param > 0)
		{
			inc = (double)param;
		}
		val *= exp (inc * 0.0001 * LN2);
		break; 

	/* Fine freq decrease (10000 donne freq / 2) */
	case	0x4:
		if (param > 0)
		{
			inc = (double)param;
		}
		val *= exp (-inc * 0.0001 * LN2);
		break;

	/* Partition */
	case	0x7:
		track_info.score.score.note = (param >> 8) & 0xFF;
		track_info.score.score.instr = param & 0xFF;
		
		/* On ne gere pas ici la nouvelle note si l'effet est un Note delay */
		if ((track_info.score.score.effect >> 8) != 0x09)
		{
			set_new_note (track_info);
		}
		break;

	/* mHz */
	case	0x8:
		if (param > 0)
		{
			val = (double)param / 1000000L;
		}
		break;

	/* Note + finetune */
	case	0xD:
		if (param > 0)
		{
			val = convert_note_to_freq ((param >> 8) & 0xFF,
			                                 (double) (param & 0xFF) / 256);
		}
		break;

	/* Note slide */
	case	0xE:
		note = (param >> 8) & 0xFF;
		speed = param & 0xFF;

		if (note != 0)
		{
			to_reach = convert_note_to_freq ((int) note, 0);
		}
		if (speed != 0)
		{
			inc = speed << 4;
		}
		break;

	/* Beat */
	case	0xF:
		fx_beat_to_freq (param, val);
		break;
	}

	return (val != old_val);
}



bool	Player::fx_param_to_freq_all_ticks (TrackInfo &track_info, LWORD param, double &val, double &inc, double to_reach)
{
	int		effect;
	double	old_val;

	old_val = val;
	effect = (int) (param >> 16) & 0xF;
	param = param & 0xFFFFL;

	switch (effect)
	{
	/* Freq increase (10000 donne freq * 2) */
	case	0x1:
		if (param > 0)
		{
			inc = (double)param;
		}
		val *= exp (inc * 0.0001 * LN2);
		break;

	/* Freq decrease (10000 donne freq / 2) */
	case	0x2:
		if (param > 0)
		{
			inc = (double)param;
		}
		val *= exp (-inc * 0.0001 * LN2);
		break;

	/* Partition */
	case	0x7:
		val = convert_period_to_freq (track_info.score.period_loc);
		break;

	/* Note slide */
	case	0xE:
		if (to_reach > val)
		{
			val *= exp (inc * 0.0001 * LN2);
			val = MIN (val, to_reach);
		}
		else
		{
			val *= exp (-inc * 0.0001 * LN2);
			val = MAX (val, to_reach);
		}
		break;

	}

	return (val != old_val);
}



bool	Player::fx_beat_to_freq (LWORD param, double &freq)
{
	long		num;
	long		denom;

	if (param <= 0)
	{
		return (false);
	}

	num = param / 100;
	denom = param - num * 100;
	if (denom <= 0)
	{
		return (false);
	}
	freq =   ((double)denom * (  ((long)_score_info.tempo_int << 16)
	                           + _score_info.tempo_frac))
	       / ((double)(num << 16) * 60);

	return (true);
}



bool	Player::fx_beat_to_time (LWORD param, double &sec)
{
	long		num;
	long		denom;

	if (param <= 0)
	{
		return (false);
	}

	num = param / 100;
	denom = param - num * 100;
	if (denom <= 0)
	{
		return (false);
	}
	sec =  (double)(num << 16) * 60
	     / ((double)denom * (  ((long)_score_info.tempo_int << 16)
	                         + _score_info.tempo_frac));

	return (true);
}



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



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