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

        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	<math.h>

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"env.h"
#include	"Envelope.h"
#include	"inst.h"
#include	"Player.h"
#include	"song.h"
#include	"splhandl.h"
#include	"WaveForm.h"



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



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



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



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



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

/* Gestion des effets */
/* Tableau de pointeurs sur des fonctions pour chaque effet */
/* Effets: 00xx-1Fxx, A0xx-CFxx, 0xxx-Fxxx */
const Player::TrackFncPtr Player::_cmd_effect_1st_tick_ptr [3] [0x60] =
{
	/* Samples */
	{
		/* 00xx - 0Fxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_detune, &Player::cmd_delay_init, &Player::cmd_cut_note_init, &Player::cmd_jump_pos,
		&Player::cmd_set_vibrato_waveform, &Player::cmd_break_pattern, &Player::cmd_set_tremolo_waveform, &Player::cmd_set_tempo,
		/* 10xx - 1Fxx */
		&Player::cmd_arpeggio_init, &Player::cmd_fine_porta_up, &Player::cmd_fine_porta_down, &Player::cmd_roll_vol_slide_init,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* A0xx - AFxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_fine_lin_vol_slide_up, &Player::cmd_fine_lin_vol_slide_down, &Player::cmd_fine_lin_master_vol_slide_up, &Player::cmd_fine_lin_master_vol_slide_down,
		&Player::cmd_set_nbr_ticks, &Player::cmd_set_fine_tempo, &Player::cmd_pattern_delay, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* B0xx - BFxx */
		&Player::cmd_tremor_init, &Player::cmd_pattern_loop, &Player::cmd_set_flags, &Player::cmd_set_vol_env,
		&Player::cmd_set_tone_env, &Player::cmd_set_pan_env, &Player::cmd_set_cutoff_env, &Player::cmd_set_reso_env,
		&Player::cmd_nop, &Player::cmd_demo_synchro, &Player::cmd_fine_sample_offset, &Player::cmd_very_fine_sample_offset,
		&Player::cmd_inc_sample_pos, &Player::cmd_dec_sample_pos, &Player::cmd_nop, &Player::cmd_nop,
		/* C0xx - CFxx */
		&Player::cmd_nop, &Player::cmd_set_mix_preset, &Player::cmd_set_lin_track_vol, &Player::cmd_set_log_track_vol,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_fine_log_track_vol_slide_up, &Player::cmd_fine_log_track_vol_slide_down, &Player::cmd_set_mix_preset_source_only, &Player::cmd_add_source_tracks,
		&Player::cmd_change_source_track_vol, &Player::cmd_change_source_track_pan, &Player::cmd_nop, &Player::cmd_nop,
		/* 0xxx - Fxxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_set_lin_vol, &Player::cmd_set_log_vol,
		&Player::cmd_set_pan, &Player::cmd_set_lin_master_vol, &Player::cmd_set_log_master_vol, &Player::cmd_roll_init,
		&Player::cmd_roll_vol_slide_set_pan_init, &Player::cmd_sample_offset, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop
	},

	/* Audio In */
	{
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_jump_pos,
		&Player::cmd_nop, &Player::cmd_break_pattern, &Player::cmd_set_tremolo_waveform, &Player::cmd_set_tempo,
		/* 10xx - 1Fxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* A0xx - AFxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_fine_lin_vol_slide_up,&Player:: cmd_fine_lin_vol_slide_down, &Player::cmd_fine_lin_master_vol_slide_up, &Player::cmd_fine_lin_master_vol_slide_down,
		&Player::cmd_set_nbr_ticks, &Player::cmd_set_fine_tempo, &Player::cmd_pattern_delay, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* B0xx - BFxx */
		&Player::cmd_tremor_init, &Player::cmd_pattern_loop, &Player::cmd_set_flags, &Player::cmd_set_vol_env,
		&Player::cmd_nop, &Player::cmd_set_pan_env, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_demo_synchro, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* C0xx - CFxx */
		&Player::cmd_nop, &Player::cmd_set_mix_preset, &Player::cmd_set_lin_track_vol, &Player::cmd_set_log_track_vol,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_fine_log_track_vol_slide_up, &Player::cmd_fine_log_track_vol_slide_down, &Player::cmd_set_mix_preset_source_only, &Player::cmd_add_source_tracks,
		&Player::cmd_change_source_track_vol, &Player::cmd_change_source_track_pan, &Player::cmd_nop, &Player::cmd_nop,
		/* 0xxx - Fxxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_set_lin_vol, &Player::cmd_set_log_vol,
		&Player::cmd_set_pan, &Player::cmd_set_lin_master_vol, &Player::cmd_set_log_master_vol, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop
	},

	/* Effets */
	{
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_detune, &Player::cmd_delay_init, &Player::cmd_cut_note_init, &Player::cmd_jump_pos,
		&Player::cmd_set_vibrato_waveform, &Player::cmd_break_pattern, &Player::cmd_set_tremolo_waveform, &Player::cmd_set_tempo,
		/* 10xx - 1Fxx */
		&Player::cmd_arpeggio_init, &Player::cmd_fine_porta_up, &Player::cmd_fine_porta_down, &Player::cmd_roll_vol_slide_init,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* A0xx - AFxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_fine_lin_vol_slide_up, &Player::cmd_fine_lin_vol_slide_down, &Player::cmd_fine_lin_master_vol_slide_up, &Player::cmd_fine_lin_master_vol_slide_down,
		&Player::cmd_set_nbr_ticks, &Player::cmd_set_fine_tempo, &Player::cmd_pattern_delay, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* B0xx - BFxx */
		&Player::cmd_tremor_init, &Player::cmd_pattern_loop, &Player::cmd_set_flags, &Player::cmd_set_vol_env,
		&Player::cmd_set_tone_env, &Player::cmd_set_pan_env, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_demo_synchro, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* C0xx - CFxx */
		&Player::cmd_nop, &Player::cmd_set_mix_preset, &Player::cmd_set_lin_track_vol, &Player::cmd_set_log_track_vol,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_fine_log_track_vol_slide_up, &Player::cmd_fine_log_track_vol_slide_down, &Player::cmd_set_mix_preset_source_only, &Player::cmd_add_source_tracks,
		&Player::cmd_change_source_track_vol, &Player::cmd_change_source_track_pan, &Player::cmd_nop, &Player::cmd_nop,
		/* 0xxx - Fxxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_set_lin_vol, &Player::cmd_set_log_vol,
		&Player::cmd_set_pan, &Player::cmd_set_lin_master_vol, &Player::cmd_set_log_master_vol,&Player:: cmd_roll_init,
		&Player::cmd_roll_vol_slide_set_pan_init, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop
	}
};

const Player::TrackFncPtr	Player::_cmd_effect_all_ticks_ptr [3] [0x60] =
{
	/* Samples */
	{
		/* 00xx - 0Fxx */
		&Player::cmd_nop, &Player::cmd_porta_up, &Player::cmd_porta_down, &Player::cmd_tone_porta,
		&Player::cmd_vibrato, &Player::cmd_tone_porta_vibrato, &Player::cmd_vibrato_tone_porta, &Player::cmd_tremolo,
		&Player::cmd_nop, &Player::cmd_delay, &Player::cmd_cut_note, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* 10xx - 1Fxx */
		&Player::cmd_arpeggio, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_roll_vol_slide,
		&Player::cmd_lin_vol_slide_up, &Player::cmd_lin_vol_slide_down, &Player::cmd_log_vol_slide_up, &Player::cmd_log_vol_slide_down,
		&Player::cmd_lin_vol_slide_up_tone_porta, &Player::cmd_lin_vol_slide_down_tone_porta, &Player::cmd_log_vol_slide_up_tone_porta, &Player::cmd_log_vol_slide_down_tone_porta,
		&Player::cmd_lin_vol_slide_up_vibrato, &Player::cmd_lin_vol_slide_down_vibrato, &Player::cmd_log_vol_slide_up_vibrato, &Player::cmd_log_vol_slide_down_vibrato,
		/* A0xx - AFxx */
		&Player::cmd_lin_master_vol_slide_up, &Player::cmd_lin_master_vol_slide_down, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_extra_fine_tone_porta,
		&Player::cmd_extra_fine_porta_up, &Player::cmd_extra_fine_porta_down, &Player::cmd_left_balance_move, &Player::cmd_right_balance_move,
		/* B0xx - BFxx */
		&Player::cmd_tremor, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_auto_tempo, &Player::cmd_auto_period,
		/* C0xx - CFxx */
		&Player::cmd_change_sample_direction, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_lin_track_vol_slide_up, &Player::cmd_lin_track_vol_slide_down, &Player::cmd_log_track_vol_slide_up, &Player::cmd_log_track_vol_slide_down,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* 0xxx - Fxxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_roll,
		&Player::cmd_roll_vol_slide_set_pan, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop
	},

	/* Audio In */
	{
		/* 00xx - 0Fxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_tremolo,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* 10xx - 1Fxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_lin_vol_slide_up, &Player::cmd_lin_vol_slide_down, &Player::cmd_log_vol_slide_up, &Player::cmd_log_vol_slide_down,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* A0xx - AFxx */
		&Player::cmd_lin_master_vol_slide_up, &Player::cmd_lin_master_vol_slide_down, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_left_balance_move, &Player::cmd_right_balance_move,
		/* B0xx - BFxx */
		&Player::cmd_tremor, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_auto_tempo, &Player::cmd_auto_period,
		/* C0xx - CFxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_lin_track_vol_slide_up, &Player::cmd_lin_track_vol_slide_down, &Player::cmd_log_track_vol_slide_up, &Player::cmd_log_track_vol_slide_down,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* 0xxx - Fxxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop
	},

	/* Effets */
	{
		/* 00xx - 0Fxx */
		&Player::cmd_nop, &Player::cmd_porta_up, &Player::cmd_porta_down, &Player::cmd_tone_porta,
		&Player::cmd_vibrato, &Player::cmd_tone_porta_vibrato, &Player::cmd_vibrato_tone_porta, &Player::cmd_tremolo,
		&Player::cmd_nop, &Player::cmd_delay, &Player::cmd_cut_note, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* 10xx - 1Fxx */
		&Player::cmd_arpeggio, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_roll_vol_slide,
		&Player::cmd_lin_vol_slide_up, &Player::cmd_lin_vol_slide_down, &Player::cmd_log_vol_slide_up, &Player::cmd_log_vol_slide_down,
		&Player::cmd_lin_vol_slide_up_tone_porta, &Player::cmd_lin_vol_slide_down_tone_porta, &Player::cmd_log_vol_slide_up_tone_porta, &Player::cmd_log_vol_slide_down_tone_porta,
		&Player::cmd_lin_vol_slide_up_vibrato, &Player::cmd_lin_vol_slide_down_vibrato, &Player::cmd_log_vol_slide_up_vibrato, &Player::cmd_log_vol_slide_down_vibrato,
		/* A0xx - AFxx */
		&Player::cmd_lin_master_vol_slide_up, &Player::cmd_lin_master_vol_slide_down, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_extra_fine_tone_porta,
		&Player::cmd_extra_fine_porta_up, &Player::cmd_extra_fine_porta_down, &Player::cmd_left_balance_move, &Player::cmd_right_balance_move,
		/* B0xx - BFxx */
		&Player::cmd_tremor, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_auto_tempo, &Player::cmd_auto_period,
		/* C0xx - CFxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_lin_track_vol_slide_up, &Player::cmd_lin_track_vol_slide_down, &Player::cmd_log_track_vol_slide_up, &Player::cmd_log_track_vol_slide_down,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		/* 0xxx - Fxxx */
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_roll,
		&Player::cmd_roll_vol_slide_set_pan, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop, &Player::cmd_nop
	}
};

/* Effets de la colonne de volume (a partir de 0x6?) */
const Player::TrackFncPtr	Player::_cmd_vol_effect_1st_tick_ptr [3] [10] =
{
	/* Samples */
	{
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_volfx_fine_lin_vol_slide_down, &Player::cmd_volfx_fine_lin_vol_slide_up,
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_volfx_set_panning, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop
	},

	/* Audio In */
	{
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_volfx_fine_lin_vol_slide_down, &Player::cmd_volfx_fine_lin_vol_slide_up,
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_volfx_set_panning, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop
	},

	/* Effects */
	{
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_volfx_fine_lin_vol_slide_down, &Player::cmd_volfx_fine_lin_vol_slide_up,
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_volfx_set_panning, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop
	}
};

const Player::TrackFncPtr	Player::_cmd_vol_effect_all_ticks_ptr [3] [10] =
{
	/* Samples */
	{
		&Player::cmd_volfx_vol_slide_down, &Player::cmd_volfx_vol_slide_up,
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_volfx_set_vibrato_speed, &Player::cmd_volfx_vibrato,
		&Player::cmd_nop, &Player::cmd_volfx_pan_slide_left,
		&Player::cmd_volfx_pan_slide_right, &Player::cmd_volfx_tone_portamento
	},

	/* Audio In */
	{
		&Player::cmd_volfx_vol_slide_down, &Player::cmd_volfx_vol_slide_up,
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_nop, &Player::cmd_volfx_pan_slide_left,
		&Player::cmd_volfx_pan_slide_right, &Player::cmd_nop
	},

	/* Effects */
	{
		&Player::cmd_volfx_vol_slide_down, &Player::cmd_volfx_vol_slide_up,
		&Player::cmd_nop, &Player::cmd_nop,
		&Player::cmd_volfx_set_vibrato_speed, &Player::cmd_volfx_vibrato,
		&Player::cmd_nop, &Player::cmd_volfx_pan_slide_left,
		&Player::cmd_volfx_pan_slide_right, &Player::cmd_volfx_tone_portamento
	}
};



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



/*==========================================================================*/
/*      Nom: cmd_manage_effects_1st_tick_spl                            */
/*      Description: Gere les effets, notes et parametres qui n'agissent    */
/*                   qu'au premier tick d'une ligne, pour une piste donnee  */
/*                   de samples.                                            */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste a traiter.                            */
/*==========================================================================*/

void	Player::cmd_manage_effects_1st_tick_spl (int track)
{
	unsigned int	effect;
	int		effect_nbr;
	int		vol_cmd;

	TrackInfo &	track_info =
		_track_info [_track_info_list [Pattern_TYPE_SPL] [track]];
	effect = track_info.score.score.effect;
	effect_nbr = effect >> 8;

	/* On ne gere pas ici la nouvelle note si l'effet est un Note delay */
	if (effect_nbr != 0x09)
	{
		set_new_note (track_info);
	}

	/* Gestion des effets de la colonne de volume */
	vol_cmd = track_info.score.score.volume >> 4;
	if (vol_cmd >= 0x06)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_vol_effect_1st_tick_ptr [Pattern_TYPE_SPL] [vol_cmd - 0x6];
		(this->*fnc_ptr) (track_info);
	}

	/* Gestion des commandes */
	if (effect_nbr < 0x20U)
	{
		track_info.score.score.effect_par = effect & 0xFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_SPL] [effect_nbr];
		(this->*fnc_ptr) (track_info);
	}
	else if (effect_nbr >= 0xA0U && effect_nbr < 0xD0U)
	{
		track_info.score.score.effect_par = effect & 0xFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_SPL] [effect_nbr - 0x80];
		(this->*fnc_ptr) (track_info);
	}
	else
	{
		track_info.score.score.effect_par = effect & 0xFFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_SPL] [(effect_nbr >> 4) + 0x50];
		(this->*fnc_ptr) (track_info);
	}
}



/*==========================================================================*/
/*      Nom: cmd_manage_effects_1st_tick_ain                            */
/*      Description: Gere les effets, notes et parametres qui n'agissent    */
/*                   qu'au premier tick d'une ligne, pour une piste donnee  */
/*                   d'Audio In.                                            */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste a traiter.                            */
/*==========================================================================*/

void	Player::cmd_manage_effects_1st_tick_ain (int track)
{
	unsigned int	effect;
	int		effect_nbr;
	int		vol_cmd;
	int		volume;

	TrackInfo &	track_info =
		_track_info [_track_info_list [Pattern_TYPE_AIN] [track]];

	/* Gestion de la colonne de volume */
	volume = track_info.score.score.volume;
	if (   volume >= 0x10
	    && volume <= 0x50)
	{
		set_lin_velocity (track_info, (volume - 0x10) << 5);
	}
	else
	{
		vol_cmd = volume >> 4;
		if (vol_cmd >= 0x06)
		{
			const TrackFncPtr	fnc_ptr =
				_cmd_vol_effect_1st_tick_ptr [Pattern_TYPE_AIN] [vol_cmd - 0x6];
			(this->*fnc_ptr) (track_info);
		}
	}

	/* Gestion des commandes */
	effect = track_info.score.score.effect;
	effect_nbr = effect >> 8;
	
	if (effect_nbr < 0x20U)
	{
		track_info.score.score.effect_par = effect & 0xFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_AIN] [effect_nbr];
		(this->*fnc_ptr) (track_info);
	}
	else if (effect_nbr >= 0xA0U && effect_nbr < 0xD0U)
	{
		track_info.score.score.effect_par = effect & 0xFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_AIN] [effect_nbr - 0x80];
		(this->*fnc_ptr) (track_info);
	}
	else
	{
		track_info.score.score.effect_par = effect & 0xFFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_AIN] [(effect_nbr >> 4) + 0x50];
		(this->*fnc_ptr) (track_info);
	}
}



/*==========================================================================*/
/*      Nom: cmd_manage_effects_1st_tick_fx                             */
/*      Description: Gere les effets, notes et parametres qui n'agissent    */
/*                   qu'au premier tick d'une ligne, pour une piste donnee  */
/*                   d'effets.                                              */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste a traiter.                            */
/*==========================================================================*/

void	Player::cmd_manage_effects_1st_tick_fx (int track)
{
	unsigned int	effect;
	int		effect_nbr;
	int		vol_cmd;

	TrackInfo	&	track_info =
		_track_info [_track_info_list [Pattern_TYPE_FX] [track]];

	/* Gestion des effets */
	fx_manage_1st_tick (track_info);

	/* Gestion des effets de la colonne de volume */
	vol_cmd = track_info.score.score.volume >> 4;
	if (vol_cmd >= 0x06)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_vol_effect_1st_tick_ptr [Pattern_TYPE_FX] [vol_cmd - 0x6];
		(this->*fnc_ptr) (track_info);
	}

	/* Gestion des commandes */
	effect = track_info.score.score.effect;
	effect_nbr = effect >> 8;

	if (effect_nbr < 0x20U)
	{
		track_info.score.score.effect_par = effect & 0xFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_FX] [effect_nbr];
		(this->*fnc_ptr) (track_info);
	}
	else if (effect_nbr >= 0xA0U && effect_nbr < 0xD0U)
	{
		track_info.score.score.effect_par = effect & 0xFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_FX] [effect_nbr - 0x80];
		(this->*fnc_ptr) (track_info);
	}
	else
	{
		track_info.score.score.effect_par = effect & 0xFFF;
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_1st_tick_ptr [Pattern_TYPE_FX] [(effect_nbr >> 4) + 0x50];
		(this->*fnc_ptr) (track_info);
	}
}



/*==========================================================================*/
/*      Nom: cmd_manage_effects_1st_tick_mid                            */
/*      Description: Gere les effets, notes et parametres qui n'agissent    */
/*                   qu'au premier tick d'une ligne, pour une piste donnee  */
/*                   MIDI.                                                  */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste a traiter.                            */
/*==========================================================================*/

void	Player::cmd_manage_effects_1st_tick_mid (int track)
{
	TrackInfo &	track_info =
		_track_info [_track_info_list [Pattern_TYPE_MID] [track]];

	/*** A faire ***/

}



/*==========================================================================*/
/*      Nom: cmd_manage_effects_all_ticks_spl                           */
/*      Description: Gere les effets qui durent toute la duree de la ligne  */
/*                   pour une piste donnee de samples.                      */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste a traiter.                            */
/*==========================================================================*/

void	Player::cmd_manage_effects_all_ticks_spl (int track)
{
	unsigned int	effect;
	int		effect_nbr;
	int		vol_cmd;

	TrackInfo	&	track_info =
		_track_info [_track_info_list [Pattern_TYPE_SPL] [track]];

	track_info.score.velocity_loc = track_info.score.velocity_lin;
	track_info.score.period_loc = track_info.score.period;

	/* Gestion des effets de la colonne de volume */
	vol_cmd = track_info.score.score.volume >> 4;
	if (vol_cmd >= 0x06)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_vol_effect_all_ticks_ptr [Pattern_TYPE_SPL] [vol_cmd - 0x06];
		(this->*fnc_ptr) (track_info);
	}

	/* Gestion des commandes */
	effect = track_info.score.score.effect;
	effect_nbr = effect >> 8;

	if (effect_nbr < 0x20U)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_SPL] [effect_nbr];
		(this->*fnc_ptr) (track_info);
	}
	else if (effect_nbr >= 0xA0U && effect_nbr < 0xD0U)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_SPL] [effect_nbr - 0x80];
		(this->*fnc_ptr) (track_info);
	}
	else
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_SPL] [(effect_nbr >> 4) + 0x50];
		(this->*fnc_ptr) (track_info);
	}
}



/*==========================================================================*/
/*      Nom: cmd_manage_effects_all_ticks_ain                           */
/*      Description: Gere les effets qui durent toute la duree de la ligne  */
/*                   pour une piste donnee d'Audio In.                      */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste a traiter.                            */
/*==========================================================================*/

void	Player::cmd_manage_effects_all_ticks_ain (int track)
{
	unsigned int	effect;
	int		effect_nbr;
	int		vol_cmd;
	TrackInfo	&	track_info =
		_track_info [_track_info_list [Pattern_TYPE_AIN] [track]];

	track_info.score.velocity_loc = track_info.score.velocity_lin;

	/* Gestion des effets de la colonne de volume */
	vol_cmd = track_info.score.score.volume >> 4;
	if (vol_cmd >= 0x06)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_vol_effect_all_ticks_ptr [Pattern_TYPE_AIN] [vol_cmd - 0x06];
		(this->*fnc_ptr) (track_info);
	}

	/* Gestion des commandes */
	effect = track_info.score.score.effect;
	effect_nbr = effect >> 8;

	if (effect_nbr < 0x20U)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_AIN] [effect_nbr];
		(this->*fnc_ptr) (track_info);
	}
	else if (effect_nbr >= 0xA0U && effect_nbr < 0xD0U)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_AIN] [effect_nbr - 0x80];
		(this->*fnc_ptr) (track_info);
	}
	else
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_AIN] [(effect_nbr >> 4) + 0x50];
		(this->*fnc_ptr) (track_info);
	}
}



/*==========================================================================*/
/*      Nom: cmd_manage_effects_all_ticks_fx                            */
/*      Description: Gere les effets qui durent toute la duree de la ligne  */
/*                   pour une piste donnee d'effets.                        */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste a traiter.                            */
/*==========================================================================*/

void	Player::cmd_manage_effects_all_ticks_fx (int track)
{
	unsigned int	effect;
	int		effect_nbr;
	int		vol_cmd;
	TrackInfo	&	track_info =
		_track_info [_track_info_list [Pattern_TYPE_FX] [track]];

	track_info.score.velocity_loc = track_info.score.velocity_lin;
	track_info.score.period_loc = track_info.score.period;

	/* Gestion des effets de la colonne de volume */
	vol_cmd = track_info.score.score.volume >> 4;
	if (vol_cmd >= 0x06)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_vol_effect_all_ticks_ptr [Pattern_TYPE_FX] [vol_cmd - 0x06];
		(this->*fnc_ptr) (track_info);
	}

	/* Gestion des commandes */
	effect = track_info.score.score.effect;
	effect_nbr = effect >> 8;

	if (effect_nbr < 0x20U)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_FX] [effect_nbr];
		(this->*fnc_ptr) (track_info);
	}
	else if (effect_nbr >= 0xA0U && effect_nbr < 0xD0U)
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_FX] [effect_nbr - 0x80];
		(this->*fnc_ptr) (track_info);
	}
	else
	{
		const TrackFncPtr	fnc_ptr =
			_cmd_effect_all_ticks_ptr [Pattern_TYPE_FX] [(effect_nbr >> 4) + 0x50];
		(this->*fnc_ptr) (track_info);
	}
}



/*==========================================================================*/
/*      Nom: cmd_manage_effects_all_ticks_mid                           */
/*      Description: Gere les effets qui durent toute la duree de la ligne  */
/*                   pour une piste donnee MIDI.                            */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste a traiter.                            */
/*==========================================================================*/

void	Player::cmd_manage_effects_all_ticks_mid (int track)
{
	TrackInfo &	track_info =
		_track_info [_track_info_list [Pattern_TYPE_MID] [track]];

	/*** A faire ***/

}



/****************************************************************************/
/*                                                                          */
/*      EFFETS, PREMIER TICK DE LA LIGNE                                    */
/*                                                                          */
/****************************************************************************/



void	Player::cmd_nop (TrackInfo &track_info)
{
	/* Rien. Que dalle. Nib. */
}



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

	val = track_info.score.score.effect_par;
	track_info.score.sample.finetune = ((val & 0xF0) >> 4) - (val & 0x0F);
	if (track_info.score.score.note != 0)
	{
		set_period (track_info,
		                 (float)convert_note_to_internal_period
			(  track_info.score.score.note
			 + track_info.score.sample.transp,
			 track_info.score.sample.finetune));
	}
}



void	Player::cmd_delay_init (TrackInfo &track_info)
{
	track_info.score.delay.play = track_info.score.score.effect_par;
}



void	Player::cmd_cut_note_init (TrackInfo &track_info)
{
	track_info.score.delay.cut = track_info.score.score.effect_par;
}



void	Player::cmd_jump_pos (TrackInfo &track_info)
{
	switch (_score_info.current_play_mode)
	{
	case	Player::MODE_SONG:
		_score_info.next_pos = track_info.score.score.effect_par;
		break;
	case	Player::MODE_STOP:
		_score_info.next_pos = 1;
		break;
	/* Player::MODE_PATTERN: pas de changement */
	}
	_score_info.next_flag = true;
}



void	Player::cmd_set_vibrato_waveform (TrackInfo &track_info)
{
	track_info.score.vibrato.waveform = track_info.score.score.effect_par & 0x03;
}



void	Player::cmd_break_pattern (TrackInfo &track_info)
{
	int		pattern;

	switch (_score_info.current_play_mode)
	{
	case	Player::MODE_SONG:
		/* Position suivante si elle n'a pas ete deja forcee */
		if (_score_info.next_flag == 0)
		{
			_score_info.next_pos = _score_info.current_pos + 1;
			if (_score_info.next_pos >= SONG_get_song_length ())
			{
				_score_info.next_pos = SONG_get_song_repeat ();
			}
		}
		pattern = SONG_get_pattern_number (_score_info.next_pos);
		break;

	case	Player::MODE_PATTERN:
		_score_info.next_pos = _score_info.current_pos;
		pattern = SONG_get_pattern_number (_score_info.next_pos);
		break;

	case	Player::MODE_STOP:
		_score_info.next_pos = 1;
		pattern = GTK_PATTERN_BIDON_B;
		break;
	}

	/* Nouvelle ligne */
	if (   track_info.score.score.effect_par
		 >= PAT_get_pattern_height (pattern))
	{
		_score_info.next_line = 0;
	}
	else
	{
		_score_info.next_line = track_info.score.score.effect_par;
	}

	_score_info.next_flag = true;
}



void	Player::cmd_set_tremolo_waveform (TrackInfo &track_info)
{
	track_info.score.tremolo.waveform = track_info.score.score.effect_par & 0x03;
}



void	Player::cmd_set_tempo (TrackInfo &track_info)
{
	if (track_info.score.score.effect_par == 0)
	{
		return;
	}

	/* Fixe la vitesse et le tempo a 125 */
	if (track_info.score.score.effect_par <= 31)
	{
		_score_info.speed = track_info.score.score.effect_par;
		_score_info.tempo = 125;
	}

	/* Fixe le tempo */
	else
	{
		_score_info.tempo = track_info.score.score.effect_par;
	}
}



void	Player::cmd_arpeggio_init (TrackInfo &track_info)
{
	track_info.score.arpeggio_cnt = 0;
}



void	Player::cmd_fine_porta_up (TrackInfo &track_info)
{
	set_period (track_info,
							 track_info.score.period
						  - (track_info.score.score.effect_par << 4));
}



void	Player::cmd_fine_porta_down (TrackInfo &track_info)
{
	set_period (track_info,
							 track_info.score.period
						  + (track_info.score.score.effect_par << 4));
}



void	Player::cmd_roll_vol_slide_init (TrackInfo &track_info)
{
	int		speed;

	speed = track_info.score.score.effect_par & 0xF;
	if (speed != 0)
	{
		track_info.score.roll.speed = speed;
		track_info.score.roll.cnt = 0;
		track_info.score.roll.nbr = -1;
	}
}



void	Player::cmd_fine_lin_vol_slide_up (TrackInfo &track_info)
{
	int		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.vol_slide.fine_speed;
	}
	track_info.score.vol_slide.fine_speed = speed;
	set_lin_velocity (track_info, track_info.score.velocity_lin + speed * 8);
}



void	Player::cmd_fine_lin_vol_slide_down (TrackInfo &track_info)
{
	int		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.vol_slide.fine_speed;
	}
	track_info.score.vol_slide.fine_speed = speed;
	set_lin_velocity (track_info, track_info.score.velocity_lin - speed * 8);
}



void	Player::cmd_fine_lin_master_vol_slide_up (TrackInfo &track_info)
{
	LWORD		vol;

	vol =   (LWORD) (_track_info + _track_info_list [GTK_TRACK_TYPE_AOU] [0])->score.track.volume_lin
	      + (LWORD) track_info.score.score.effect_par;
	set_lin_master_volume (vol);
}



void	Player::cmd_fine_lin_master_vol_slide_down (TrackInfo &track_info)
{
	LWORD		vol;

	vol =   (LWORD) (_track_info + _track_info_list [GTK_TRACK_TYPE_AOU] [0])->score.track.volume_lin
	      - (LWORD) track_info.score.score.effect_par;
	set_lin_master_volume (vol);
}



void	Player::cmd_set_nbr_ticks (TrackInfo &track_info)
{
	if (track_info.score.score.effect_par != 0)
	{
		_score_info.speed = track_info.score.score.effect_par;
	}
}



void	Player::cmd_set_fine_tempo (TrackInfo &track_info)
{
	_score_info.tempo =
		  floor (_score_info.tempo)
		+ track_info.score.score.effect_par * (1.0 / 256);
}



void	Player::cmd_pattern_delay (TrackInfo &track_info)
{
	_score_info.pattern_delay = track_info.score.score.effect_par;
}



void	Player::cmd_tremor_init (TrackInfo &track_info)
{
	int		trem_on;
	int		trem_off;

	if (track_info.score.score.effect_par == 0)
	{
		return;
	}

	trem_on = track_info.score.score.effect_par >> 4;
	trem_off = track_info.score.score.effect_par & 15;
	track_info.score.tremor.on = trem_on;
	track_info.score.tremor.period = trem_on + trem_off;
	track_info.score.tremor.cnt = 0;
}



void	Player::cmd_pattern_loop (TrackInfo &track_info)
{
	int		nbr;
	int		count;

	nbr = track_info.score.score.effect_par;

	/* Debut de la boucle: on memorise la position de bouclage */
	if (nbr == 0)
	{
		track_info.score.ploop.songpos = _score_info.current_pos;
		track_info.score.ploop.linepos = _score_info.current_line;
		return;
	}

	count = track_info.score.ploop.cnt;

	/* Premiere fin de boucle: memorise le nombre de repetitions */
	if (count == 0)
	{
		count = nbr;
	}
	else
	{
		count --;
	}
	track_info.score.ploop.cnt = count;

	/* Bouclage */
	if (   count > 0
	    && track_info.score.ploop.songpos >= 0)
	{
		_score_info.next_line = track_info.score.ploop.linepos;
		_score_info.next_pos = track_info.score.ploop.songpos;
		_score_info.next_flag = true;
	}
}



void	Player::cmd_set_flags (TrackInfo &track_info)
{
	int		track;

	/* Interpolation sur aucune piste */
	if (track_info.score.score.effect_par & 0x04)
	{
		for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; track ++)
		{
			set_interpolation_flag (track, false);
		}
	}

	/* Interpolation sur toutes les pistes */
	if (track_info.score.score.effect_par & 0x02)
	{
		for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; track ++)
		{
			set_interpolation_flag (track, true);
		}
	}

	/* Interpolation */
	const bool		interp_flag =
		((track_info.score.score.effect_par & 0x01) != 0);
	set_interpolation_flag (track_info, interp_flag);
}



void	Player::cmd_set_vol_env (TrackInfo &track_info)
{
	if (track_info.score.score.effect_par > INST_NBRINSTR_MAXI)
	{
		return;
	}

	Voice &			voice = use_main_voice (track_info);

	voice._env_set._com_env [Envelope_TYPE_VOL].nbr =
		track_info.score.score.effect_par;
	if (track_info.score.score.effect_par != 0)
	{
		ENV_start (
			Envelope_TYPE_VOL,
		   voice._env_set._com_env [Envelope_TYPE_VOL].nbr,
		   voice._env_set._com_env [Envelope_TYPE_VOL].proc
		);
	}
}



void	Player::cmd_set_tone_env (TrackInfo &track_info)
{
	if (track_info.score.score.effect_par > INST_NBRINSTR_MAXI)
	{
		return;
	}

	Voice &			voice = use_main_voice (track_info);

	voice._env_set._com_env [Envelope_TYPE_TON].nbr =
		track_info.score.score.effect_par;
	if (track_info.score.score.effect_par != 0)
	{
		ENV_start (
			Envelope_TYPE_TON,
		   voice._env_set._com_env [Envelope_TYPE_TON].nbr,
		   voice._env_set._com_env [Envelope_TYPE_TON].proc
		);
	}
}



void	Player::cmd_set_pan_env (TrackInfo &track_info)
{
	if (track_info.score.score.effect_par > INST_NBRINSTR_MAXI)
	{
		return;
	}

	Voice &			voice = use_main_voice (track_info);

	voice._env_set._com_env [Envelope_TYPE_PAN].nbr =
		track_info.score.score.effect_par;
	if (track_info.score.score.effect_par != 0)
	{
		ENV_start (
			Envelope_TYPE_PAN,
		   voice._env_set._com_env [Envelope_TYPE_PAN].nbr,
		   voice._env_set._com_env [Envelope_TYPE_PAN].proc
		);
	}
}



void	Player::cmd_set_cutoff_env (TrackInfo &track_info)
{
	if (track_info.score.score.effect_par > INST_NBRINSTR_MAXI)
	{
		return;
	}

	Voice &			voice = use_main_voice (track_info);

	voice._env_set._com_env [Envelope_TYPE_CUTOFF].nbr =
		track_info.score.score.effect_par;
	if (track_info.score.score.effect_par != 0)
	{
		track_info.score.lpf.filter_flag = true;
		ENV_start (
			Envelope_TYPE_CUTOFF,
		   voice._env_set._com_env [Envelope_TYPE_CUTOFF].nbr,
		   voice._env_set._com_env [Envelope_TYPE_CUTOFF].proc
		);
	}
}



void	Player::cmd_set_reso_env (TrackInfo &track_info)
{
	if (track_info.score.score.effect_par > INST_NBRINSTR_MAXI)
	{
		return;
	}

	Voice &			voice = use_main_voice (track_info);

	voice._env_set._com_env [Envelope_TYPE_RESO].nbr = track_info.score.score.effect_par;
	if (track_info.score.score.effect_par != 0)
	{
		track_info.score.lpf.filter_flag = true;
		ENV_start (
			Envelope_TYPE_RESO,
		   voice._env_set._com_env [Envelope_TYPE_RESO].nbr,
		   voice._env_set._com_env [Envelope_TYPE_RESO].proc
		);
	}
}



void	Player::cmd_demo_synchro (TrackInfo &track_info)
{
	/* Rien. Que dalle. Nib. */
}



void	Player::cmd_fine_sample_offset (TrackInfo &track_info)
{
	cmd_do_sample_offset (
		track_info,
	   (LWORD)track_info.score.score.effect_par << 4,
	   0
	);
}



void	Player::cmd_very_fine_sample_offset (TrackInfo &track_info)
{
	cmd_do_sample_offset (
		track_info,
	   (LWORD)track_info.score.score.effect_par,
	   0
	);
}



void	Player::cmd_inc_sample_pos (TrackInfo &track_info)
{
	if (track_info.track_type == Pattern_TYPE_SPL)
	{
		Voice &			voice = use_main_voice (track_info);

		SLWORD			new_pos = voice.spl.get_playback_pos_int ();
		ULWORD			pos_frac = voice.spl.get_playback_pos_frac ();
		const LWORD		reppos = voice.spl.reppos;
		const LWORD		replen = voice.spl.replen;
		const LWORD		length = reppos + replen;
		bool				back_flag = voice.spl.is_running_backward ();

		/* Bouclage forward */
		if (voice.spl.loopmode == WaveForm_LOOP_TYPE_NONE)
		{
			if (back_flag)
			{
				new_pos -= (LWORD)track_info.score.score.effect_par;
			}
			else
			{
				new_pos += (LWORD)track_info.score.score.effect_par;
				if (new_pos > length)
				{
					new_pos -= reppos;
					new_pos %= replen;
					new_pos += reppos;
				}
			}
		}

		/* Ping-Pong Loop */
		else if (voice.spl.loopmode == WaveForm_LOOP_TYPE_PP)
		{
			if (back_flag)
			{
				new_pos -= (LWORD)track_info.score.score.effect_par;
				/* Si on depasse a gauche, on remet dans la direction positive */
				if (new_pos < reppos - 1)
				{
					back_flag = false;
					new_pos = (reppos * 2 - 1) - new_pos;
					if (pos_frac != 0)
					{
						pos_frac = ULWORD (- SLWORD (pos_frac));
						new_pos --;
					}
				}
			}
			else
			{
				new_pos += (LWORD)track_info.score.score.effect_par;
			}

			/* Si on depasse a gauche */
			if (new_pos > length)
			{
				new_pos -= reppos;
				new_pos %= replen * 2;
				if (new_pos >= replen)
				{
					back_flag = true;
					new_pos = (replen * 2 - 1) - new_pos;
					if (pos_frac != 0)
					{
						new_pos --;
						pos_frac = ULWORD (- SLWORD (pos_frac));
					}
				}
				new_pos += reppos;
			}
		}

		/* Pas de bouclage */
		else
		{
			if (back_flag)
			{
				new_pos -= (LWORD)track_info.score.score.effect_par;
			}
			else
			{
				new_pos += (LWORD)track_info.score.score.effect_par;
			}
		}

		voice.spl.set_playback_pos (new_pos, pos_frac);
		voice.spl.set_direction (back_flag);
	}
}



void	Player::cmd_dec_sample_pos (TrackInfo &track_info)
{
	if (track_info.track_type == Pattern_TYPE_SPL)
	{
		Voice &			voice = use_main_voice (track_info);

		SLWORD			new_pos = voice.spl.get_playback_pos_int ();
		ULWORD			pos_frac = voice.spl.get_playback_pos_frac ();
		const LWORD		reppos = voice.spl.reppos;
		const LWORD		replen = voice.spl.replen;
		const LWORD		length = reppos + replen;
		bool				back_flag = voice.spl.is_running_backward ();

		/* Marche arriere */
		if (back_flag)
		{
			/* Bouclage forward */
			if (voice.spl.loopmode == WaveForm_LOOP_TYPE_NONE)
			{
				new_pos += (LWORD)track_info.score.score.effect_par;
				if (new_pos > length)
				{
					new_pos -= reppos;
					new_pos %= replen;
					new_pos += reppos;
				}
			}

			/* Ping-Pong Loop */
			else if (voice.spl.loopmode == WaveForm_LOOP_TYPE_PP)
			{
				new_pos += (LWORD)track_info.score.score.effect_par;
				/* Si on depasse a gauche */
				if (new_pos > length)
				{
					new_pos -= reppos;
					new_pos %= replen * 2;
					if (new_pos >= replen)
					{
						back_flag = false;
						new_pos = (replen * 2 - 1) - new_pos;
						if (pos_frac != 0)
						{
							new_pos --;
							pos_frac = ULWORD (- SLWORD (pos_frac));
						}
					}
					new_pos += reppos;
				}
			}

			/* Pas de bouclage */
			else
			{
				new_pos += (LWORD)track_info.score.score.effect_par;
				new_pos = MIN (new_pos, length - 1);
			}
		}

		/* Marche avant */
		else
		{
			new_pos -= (LWORD)track_info.score.score.effect_par;
			new_pos = MAX (new_pos, 0);
		}

		voice.spl.set_playback_pos (new_pos, pos_frac);
		voice.spl.set_direction (back_flag);
	}
}



void	Player::cmd_set_mix_preset (TrackInfo &track_info)
{
	cmd_do_set_mix_preset (
		track_info,
	   track_info.score.score.effect_par,
	   MIX_PRESET_REPL
	);
}



void	Player::cmd_set_lin_track_vol (TrackInfo &track_info)
{
	set_lin_volume (track_info, track_info.score.score.effect_par << 8);
}



void	Player::cmd_set_log_track_vol (TrackInfo &track_info)
{
	set_log_volume (track_info, track_info.score.score.effect_par << 4);
}



void	Player::cmd_fine_log_track_vol_slide_up (TrackInfo &track_info)
{
	LWORD		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.track.log_vol_fine_speed;
	}
	track_info.score.track.log_vol_fine_speed = speed;
	set_log_volume (track_info, track_info.score.track.volume_log + speed);
}



void	Player::cmd_fine_log_track_vol_slide_down (TrackInfo &track_info)
{
	LWORD		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.track.lin_vol_fine_speed;
	}
	track_info.score.track.log_vol_fine_speed = speed;
	set_log_volume (track_info, track_info.score.track.volume_log - speed);
}



void	Player::cmd_set_mix_preset_source_only (TrackInfo &track_info)
{
	cmd_do_set_mix_preset (
		track_info,
	   track_info.score.score.effect_par,
	   MIX_PRESET_REPL | MIX_PRESET_NO_DEST
	);
}



void	Player::cmd_add_source_tracks (TrackInfo &track_info)
{
	cmd_do_set_mix_preset (
		track_info,
		track_info.score.score.effect_par,
	   MIX_PRESET_ADD | MIX_PRESET_NO_DEST
	);
}



void	Player::cmd_change_source_track_vol (TrackInfo &track_info)
{
	cmd_do_set_mix_preset (
		track_info,
	   track_info.score.score.effect_par,
	   MIX_PRESET_ADD | MIX_PRESET_NO_DEST | MIX_PRESET_NO_PAN
	);
}



void	Player::cmd_change_source_track_pan (TrackInfo &track_info)
{
	cmd_do_set_mix_preset (
		track_info,
	   track_info.score.score.effect_par,
	   MIX_PRESET_ADD | MIX_PRESET_NO_DEST | MIX_PRESET_NO_VOL
	);
}



void	Player::cmd_set_lin_vol (TrackInfo &track_info)
{
	set_lin_velocity (track_info, track_info.score.score.effect_par * 8);
}



void	Player::cmd_set_log_vol (TrackInfo &track_info)
{
	set_log_velocity (track_info, track_info.score.score.effect_par);
}



void	Player::cmd_set_pan (TrackInfo &track_info)
{
	set_panning (track_info, false, track_info.score.score.effect_par << 4);
}



void	Player::cmd_set_lin_master_vol (TrackInfo &track_info)
{
	set_lin_master_volume (track_info.score.score.effect_par);
}



void	Player::cmd_set_log_master_vol (TrackInfo &track_info)
{
	set_log_master_volume (track_info.score.score.effect_par);
}



void	Player::cmd_roll_init (TrackInfo &track_info)
{
	int		speed;

	/* Nombre de coups */
	if ((track_info.score.score.effect_par & 0xFF) == 0)
	{
		track_info.score.roll.nbr = -1;
	}
	else
	{
		track_info.score.roll.nbr = track_info.score.score.effect_par & 0xFF;
	}

	/* Vitesse */
	speed = track_info.score.score.effect_par >> 8;
	if (speed != 0)
	{
		track_info.score.roll.speed = speed;
		track_info.score.roll.cnt = 0;
	}
}



void	Player::cmd_roll_vol_slide_set_pan_init (TrackInfo &track_info)
{
	int		speed;

	/* Balance */
	track_info.score.track.panning = (track_info.score.score.effect_par & 0xF00) << 4;

	/* Vitesse */
	speed = track_info.score.score.effect_par & 0xF;
	if (speed != 0)
	{
		track_info.score.roll.speed = speed;
		track_info.score.roll.cnt = 0;
		track_info.score.roll.nbr = -1;
	}
}



void	Player::cmd_sample_offset (TrackInfo &track_info)
{
	cmd_do_sample_offset (
		track_info,
	   (LWORD)track_info.score.score.effect_par << 8,
	   0
	);
}



/****************************************************************************/
/*                                                                          */
/*      EFFETS, TOUS LES TICKS                                              */
/*                                                                          */
/****************************************************************************/



void	Player::cmd_porta_up (TrackInfo &track_info)
{
	SLWORD	speed;

	speed = track_info.score.score.effect_par << 4;
	if (speed == 0)
	{
		speed = track_info.score.porta.speed;
	}
	track_info.score.porta.speed = speed;

	set_period (track_info, track_info.score.period - speed);
}



void	Player::cmd_porta_down (TrackInfo &track_info)
{
	SLWORD	speed;

	speed = track_info.score.score.effect_par << 4;
	if (speed == 0)
	{
		speed = track_info.score.porta.speed;
	}
	track_info.score.porta.speed = speed;

	set_period (track_info, track_info.score.period + speed);
}



void	Player::cmd_tone_porta (TrackInfo &track_info)
{
	cmd_do_tone_porta (track_info,
								  track_info.score.score.effect_par << 4);
}



void	Player::cmd_vibrato (TrackInfo &track_info)
{
	cmd_do_vibrato (track_info,
							  track_info.score.score.effect_par);
}



void	Player::cmd_tone_porta_vibrato (TrackInfo &track_info)
{
	cmd_do_tone_porta (track_info,
								  track_info.score.score.effect_par << 4);
	cmd_do_vibrato (track_info, 0);
}



void	Player::cmd_vibrato_tone_porta (TrackInfo &track_info)
{
	cmd_do_tone_porta (track_info, 0);
	cmd_do_vibrato (track_info,
							  track_info.score.score.effect_par);
}



void	Player::cmd_tremolo (TrackInfo &track_info)
{
	int		speed;
	int		amplitude;
	int		new_velo;

	/* Vitesse du Tremolo */
	speed = (track_info.score.score.effect_par & 0xF0) >> 2;
	if (speed == 0)
	{
		speed = track_info.score.tremolo.speed;
	}
	track_info.score.tremolo.speed = speed;

	/* Amplitude du Tremolo */
	amplitude = track_info.score.score.effect_par & 0xF;
	if (amplitude == 0)
	{
		amplitude = track_info.score.tremolo.amp;
	}
	track_info.score.tremolo.amp = amplitude;

	/* Reglage de la velocite locale */
	new_velo =   track_info.score.velocity_lin
				  + (SWORD) ((  Player::_sin_table [track_info.score.tremolo.waveform]
	                                           [(track_info.score.tremolo.cnt >> 2) & 0x3F]
								  * amplitude) / 2);
	new_velo = MIN (new_velo, 0x800);
	new_velo = MAX (new_velo, 0);
	track_info.score.velocity_loc = new_velo;
	track_info.score.changed_volume_flag = true;

	/* Compteur suivant */
	track_info.score.vibrato.cnt += (UBYTE)speed;
}



void	Player::cmd_delay (TrackInfo &track_info)
{
	if (track_info.score.delay.play == _score_info.tick_counter)
	{
		set_new_note (track_info);
	}
}



void	Player::cmd_cut_note (TrackInfo &track_info)
{
	if (track_info.score.delay.cut == _score_info.tick_counter)
	{
		Voice &			voice = use_main_voice (track_info);

		/* Pas d'enveloppe de volume, on coupe simplement le sifflet */
		if (voice._env_set._com_env [Envelope_TYPE_VOL].nbr == 0)
		{
			set_lin_velocity (track_info, 0);
		}

		/* Sinon on passe toutes les enveloppes en key off. */
		else
		{
			for (int type = 0; type < Envelope_NBR_TYPES; type ++)
			{
				voice._env_set._com_env [type].proc.key_off_flag = 1;
			}
		}
	}
}



void	Player::cmd_arpeggio (TrackInfo &track_info)
{
	int		count;
	int		transp;
	int		note;

	count = track_info.score.arpeggio_cnt;

	if (count == 0)
	{
		count = 1;
	}

	else
	{
		if (count == 1)
		{
			transp = (track_info.score.score.effect_par >> 4) & 0xF;
			count = 2;
		}
		else if (count == 2)
		{
			transp = track_info.score.score.effect_par & 0xF;
			count = 0;
		}

		note =   track_info.score.note
				 + (signed int)track_info.score.sample.transp
				 + transp;
		note = MIN (note, GTK_NBRNOTES_MAXI - 1);
		note = MAX (note, 1);
		track_info.score.period_loc = float (convert_note_to_internal_period (
			note,
			track_info.score.sample.finetune
		));
	}

	track_info.score.arpeggio_cnt = count;
}



void	Player::cmd_roll_vol_slide (TrackInfo &track_info)
{
	int		count;
	int		cmd;
	int		vol;

	/* Roulement */
	count = track_info.score.roll.cnt;
	if (count == 0)
	{
		if (track_info.track_type == Pattern_TYPE_SPL)
		{
			retrig_note (track_info);
		}

		/* Changement de volume */
		cmd = (track_info.score.score.effect_par >> 4) & 0xF;
		vol = track_info.score.velocity_lin;

		/* 0, -4, -8, -16, -32, -64 */
		if (cmd <= 5)
		{
			vol -= (16 << cmd) & ~4;
		}

		/* x2/3 */
		else if (cmd == 6)
		{
			vol = (vol << 1) / 3;
		}

		/* x1/2 */
		else if (cmd == 7)
		{
			vol >>= 1;
		}

		/* 0, +4, +8, +16, +32, +64 */
		else if (cmd <= 13)
		{
			vol += (16 << (cmd & 0x7)) & ~4;
		}

		/* x3/2 */
		else if (cmd == 14)
		{
			vol = (vol * 3) >> 1;
		}

		/* x2 */
		else
		{
			vol <<= 1;
		}

		set_lin_velocity (track_info, vol);
	}

	/* Tick suivant */
	count ++;
	if (count >= track_info.score.roll.speed)
	{
		count = 0;
	}
	track_info.score.roll.cnt = count;
}



void	Player::cmd_change_sample_direction (TrackInfo &track_info)
{
	if (track_info.track_type == Pattern_TYPE_SPL)
	{
		Voice &			voice = use_main_voice (track_info);

		// Right tick ?
		if ((track_info.score.score.effect_par & 0x0F) != _score_info.tick_counter)
		{
			return;
		}

		// Terminated ?
		if (! voice.spl.is_active ())
		{
			return;
		}

		// Finds the new direction
		bool				back_flag = voice.spl.is_running_backward ();
		int				mode = track_info.score.score.effect_par >> 4;
		if (mode == 1)
		{
			back_flag = false;
		}
		else if (mode == 2)
		{
			back_flag = true;
		}
		else
		{
			back_flag = ! back_flag;
		}

		// Special cases
		long				length = voice.spl.reppos + voice.spl.replen;
		long				pos_int = voice.spl.get_playback_pos_int ();
		ULWORD			pos_frac = voice.spl.get_playback_pos_frac ();
		if (pos_frac == 0 && length > 0)
		{
			/* Le sample n'a pas encore commence, on le passe a l'envers
				depuis la fin. */
			if (pos_int <= 0)
			{
				pos_int = length - 1;
				back_flag = true;
			}

			/* Inversement, si on est a la fin d'un sample en ping-pong loop,
				on se positionne au debut du sample. */
			else if (   pos_int >= length
						&& voice.spl.loopmode == WaveForm_LOOP_TYPE_PP)
			{
				pos_int = 0;
				back_flag = false;
			}

			voice.spl.set_playback_pos (pos_int, pos_frac);
		}

		// Applies the changes
		voice.spl.set_direction (back_flag);
	}
}



void	Player::cmd_roll (TrackInfo &track_info)
{
	int		count;
	int		nbr;

	nbr = track_info.score.roll.nbr;
	if (nbr == 0)
	{
		return;
	}

	count = track_info.score.roll.cnt;
	if (count == 0)
	{
		if (track_info.track_type == Pattern_TYPE_SPL)
		{
			retrig_note (track_info);
		}
		if (nbr > 0)
		{
			track_info.score.roll.nbr = nbr - 1;
		}
	}

	count ++;
	if (count >= track_info.score.roll.speed)
	{
		count = 0;
	}
	track_info.score.roll.cnt = count;
}



void	Player::cmd_roll_vol_slide_set_pan (TrackInfo &track_info)
{
	cmd_roll_vol_slide (track_info);
}



void	Player::cmd_lin_vol_slide_up (TrackInfo &track_info)
{
	cmd_do_lin_vol_slide_up (track_info, track_info.score.score.effect_par);
}



void	Player::cmd_lin_vol_slide_down (TrackInfo &track_info)
{
	cmd_do_lin_vol_slide_down (track_info, track_info.score.score.effect_par);
}



void	Player::cmd_log_vol_slide_up (TrackInfo &track_info)
{
	int		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.vol_slide.speed;
	}
	track_info.score.vol_slide.speed = speed;
	set_log_velocity (track_info, track_info.score.velocity_log + speed);
}



void	Player::cmd_log_vol_slide_down (TrackInfo &track_info)
{
	int		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.vol_slide.speed;
	}
	track_info.score.vol_slide.speed = speed;
	set_log_velocity (track_info, track_info.score.velocity_log - speed);
}



void	Player::cmd_lin_vol_slide_up_tone_porta (TrackInfo &track_info)
{
	cmd_do_lin_vol_slide_up (track_info, track_info.score.score.effect_par);
	cmd_do_tone_porta (track_info, 0);
}



void	Player::cmd_lin_vol_slide_down_tone_porta (TrackInfo &track_info)
{
	cmd_do_lin_vol_slide_down (track_info, track_info.score.score.effect_par);
	cmd_do_tone_porta (track_info, 0);
}



void	Player::cmd_log_vol_slide_up_tone_porta (TrackInfo &track_info)
{
	cmd_log_vol_slide_up (track_info);
	cmd_do_tone_porta (track_info, 0);
}



void	Player::cmd_log_vol_slide_down_tone_porta (TrackInfo &track_info)
{
	cmd_log_vol_slide_down (track_info);
	cmd_do_tone_porta (track_info, 0);
}



void	Player::cmd_lin_vol_slide_up_vibrato (TrackInfo &track_info)
{
	cmd_do_lin_vol_slide_up (track_info, track_info.score.score.effect_par);
	cmd_do_vibrato (track_info, 0);
}



void	Player::cmd_lin_vol_slide_down_vibrato (TrackInfo &track_info)
{
	cmd_do_lin_vol_slide_down (track_info, track_info.score.score.effect_par);
	cmd_do_vibrato (track_info, 0);
}



void	Player::cmd_log_vol_slide_up_vibrato (TrackInfo &track_info)
{
	cmd_log_vol_slide_up (track_info);
	cmd_do_vibrato (track_info, 0);
}



void	Player::cmd_log_vol_slide_down_vibrato (TrackInfo &track_info)
{
	cmd_log_vol_slide_down (track_info);
	cmd_do_vibrato (track_info, 0);
}



void	Player::cmd_lin_master_vol_slide_up (TrackInfo &track_info)
{
	set_lin_master_volume (  (LWORD) (  _track_info
														+ _track_info_list [GTK_TRACK_TYPE_AOU] [0])->score.track.volume_lin
										 + (LWORD) track_info.score.score.effect_par);
}

void	Player::cmd_lin_master_vol_slide_down (TrackInfo &track_info)
{
	set_lin_master_volume (  (LWORD) (  _track_info
														+ _track_info_list [GTK_TRACK_TYPE_AOU] [0])->score.track.volume_lin
										 - (LWORD) track_info.score.score.effect_par);
}



void	Player::cmd_extra_fine_tone_porta (TrackInfo &track_info)
{
	cmd_do_tone_porta (track_info, track_info.score.score.effect_par);
}



void	Player::cmd_extra_fine_porta_up (TrackInfo &track_info)
{
	LWORD		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.porta.speed;
	}
	track_info.score.porta.speed = speed;

	set_period (track_info, track_info.score.period - speed);
}



void	Player::cmd_extra_fine_porta_down (TrackInfo &track_info)
{
	LWORD		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.porta.speed;
	}
	track_info.score.porta.speed = speed;

	set_period (track_info, track_info.score.period + speed);
}



void	Player::cmd_left_balance_move (TrackInfo &track_info)
{
	long		speed;

	speed = (long)track_info.score.score.effect_par << 8;
	cmd_do_pan_slide_left (track_info, speed);
}



void	Player::cmd_right_balance_move (TrackInfo &track_info)
{
	long		speed;

	speed = (long)track_info.score.score.effect_par << 8;
	cmd_do_pan_slide_right (track_info, speed);
}



void	Player::cmd_tremor (TrackInfo &track_info)
{
	int		count;

	count = track_info.score.tremor.cnt;

	/* Mettre le volume a 0 ? */
	if (count >= track_info.score.tremor.on)
	{
		track_info.score.velocity_loc = 0;
	}

	/* Tick suivant */
	count ++;
	if (count >= track_info.score.tremor.period)
	{
		count = 0;
	}
	track_info.score.tremor.cnt = count;
}



void	Player::cmd_auto_tempo (TrackInfo &track_info)
{
	if (track_info.track_type == Pattern_TYPE_SPL)
	{
		double	val_2;
		double	val_1;

		if (   _score_info.tick_counter != 0
			 || track_info.score.score.effect_par == 0)
		{
			return;
		}

		Voice &			voice = use_main_voice (track_info);

	/*
				  60s * lignes * speed   frequence_spl   periode_ref
		tempo = -------------------- * ------------- * ------------
					4 lignes * 6 ticks    longueur_spl    periode_note
	*/

		val_1 =   (double) (  (long)track_info.score.score.effect_par
		                    * _score_info.speed * 60)
				  * (  convert_period_to_rel_freq (track_info.score.period)
		           * voice.spl.freq);

		if (voice.spl.loopmode == WaveForm_LOOP_TYPE_FWD)
		{
			val_2 =   (double)voice.spl.replen * (4 * 6);
		}
		else if (voice.spl.loopmode == WaveForm_LOOP_TYPE_PP)
		{
			val_2 =   (double)(voice.spl.replen << 1) * (4 * 6);
		}
		else
		{
			val_2 =   (double) (voice.spl.reppos + voice.spl.replen)
					  * (4 * 6);
		}

		if (val_2 <= 0)
		{
			return;
		}

		val_1 /= val_2;

		/* Si le tempo est trop rapide, on le divise par 2 */
		while (val_1 > MAX_TEMPO)
		{
			val_1 /= 2;
		}

		set_tempo (val_1);
	}
}



void	Player::cmd_auto_period (TrackInfo &track_info)
{
	if (track_info.track_type == Pattern_TYPE_SPL)
	{
		double	val_1;
		double	val_2;

		if (   _score_info.tick_counter != 0
			 || track_info.score.score.effect_par == 0)
		{
			return;
		}


		Voice &			voice = use_main_voice (track_info);
/*
						60s * lignes * speed   frequence_spl   periode_ref
	periode_note = -------------------- * ------------- * -----------
						 4 lignes * 6 ticks    longueur_spl       tempo
*/

		if (voice.spl.loopmode == WaveForm_LOOP_TYPE_FWD)
		{
			val_1 = voice.spl.replen;
		}
		else if (voice.spl.loopmode == WaveForm_LOOP_TYPE_PP)
		{
			val_1 = voice.spl.replen << 1;
		}
		else
		{
			val_1 = voice.spl.reppos + voice.spl.replen;
		}

		if (val_1 <= 0)
		{
			return;
		}

		val_2 =   _score_info.tempo * (4 * 6 / 60.0) * val_1
		        / (  (_score_info.speed * track_info.score.score.effect_par)
		           * double (voice.spl.freq));

		val_1 = convert_rel_freq_to_period (val_2);
		val_1 = MIN (val_1, Player::MAX_PERIOD);
		val_1 = MAX (val_1, Player::MIN_PERIOD);
		track_info.score.period = (float) val_1;
		track_info.score.period_loc = (float) val_1;
	}
}



void	Player::cmd_lin_track_vol_slide_up (TrackInfo &track_info)
{
	LWORD		speed;

	speed = track_info.score.score.effect_par << 4;
	if (speed == 0)
	{
		speed = track_info.score.track.lin_vol_speed;
	}
	track_info.score.track.lin_vol_speed = speed;
	set_lin_volume (track_info, track_info.score.track.volume_lin + speed);
}



void	Player::cmd_lin_track_vol_slide_down (TrackInfo &track_info)
{
	LWORD		speed;

	speed = track_info.score.score.effect_par << 4;
	if (speed == 0)
	{
		speed = track_info.score.track.lin_vol_speed;
	}
	track_info.score.track.lin_vol_speed = speed;
	set_lin_volume (track_info, track_info.score.track.volume_lin - speed);
}



void	Player::cmd_log_track_vol_slide_up (TrackInfo &track_info)
{
	LWORD		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.track.log_vol_speed;
	}
	track_info.score.track.log_vol_speed = speed;
	set_log_volume (track_info, track_info.score.track.volume_log + speed);
}



void	Player::cmd_log_track_vol_slide_down (TrackInfo &track_info)
{
	LWORD		speed;

	speed = track_info.score.score.effect_par;
	if (speed == 0)
	{
		speed = track_info.score.track.log_vol_speed;
	}
	track_info.score.track.log_vol_speed = speed;
	set_log_volume (track_info, track_info.score.track.volume_log - speed);
}



/****************************************************************************/
/*                                                                          */
/*      EFFETS DE LA COLONNE DE VOLUME, PREMIER TICK DE LA LIGNE            */
/*                                                                          */
/****************************************************************************/



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

	param = track_info.score.score.volume & 0x0F;
	cmd_do_lin_vol_slide_down (track_info, param << 2);
}



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

	param = track_info.score.score.volume & 0x0F;
	cmd_do_lin_vol_slide_up (track_info, param << 2);
}



void	Player::cmd_volfx_set_panning (TrackInfo &track_info)
{
	long		param;

	param = track_info.score.score.volume & 0x0F;
	set_panning (track_info, false, param << 12);
}



/****************************************************************************/
/*                                                                          */
/*      EFFETS DE LA COLONNE DE VOLUME, TOUS LES TICKS                      */
/*                                                                          */
/****************************************************************************/



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

	param = track_info.score.score.volume & 0x0F;
	cmd_do_lin_vol_slide_down (track_info, param << 2);
}



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

	param = track_info.score.score.volume & 0x0F;
	cmd_do_lin_vol_slide_up (track_info, param << 2);
}



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

	param = track_info.score.score.volume & 0x0F;
	cmd_do_vibrato (track_info, param << 4);
}



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

	param = track_info.score.score.volume & 0x0F;
	cmd_do_vibrato (track_info, param);
}



void	Player::cmd_volfx_pan_slide_left (TrackInfo &track_info)
{
	long		param;

	param = track_info.score.score.volume & 0x0F;
	cmd_do_pan_slide_left (track_info, param << 12);
}



void	Player::cmd_volfx_pan_slide_right (TrackInfo &track_info)
{
	long		param;

	param = track_info.score.score.volume & 0x0F;
	cmd_do_pan_slide_right (track_info, param << 12);
}



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

	param = track_info.score.score.volume & 0x0F;
	cmd_do_tone_porta (track_info, param << 4);
}



/****************************************************************************/
/*                                                                          */
/*      FONCTIONS COMMUNES A PLUSIEURS EFFETS                               */
/*                                                                          */
/****************************************************************************/



void	Player::cmd_do_sample_offset (TrackInfo &track_info, LWORD pos_int, ULWORD pos_frac)
{
	if (track_info.track_type == Pattern_TYPE_SPL)
	{
		if (pos_int == 0 && pos_frac == 0)
		{
			pos_int  = track_info.score.offset.pos_int;
			pos_frac = track_info.score.offset.pos_frac;
		}
		track_info.score.offset.pos_int  = pos_int;
		track_info.score.offset.pos_frac = pos_frac;

		Voice *			voice_ptr = &(use_main_voice (track_info));

		/* Verifie qu'on est bien dans la bonne zone */
		pos_int = MIN (pos_int, voice_ptr->spl.reppos + voice_ptr->spl.replen - 1);
		pos_int = MAX (pos_int, 0);

		// Check if we shift from a significant amount. If so, we retrig a note.
		const long		pos_int_old = voice_ptr->spl.get_playback_pos_int ();
		if (std::abs (pos_int - pos_int_old) > 32)
		{
			retrig_note (track_info);
			voice_ptr = &(use_main_voice (track_info));
		}

		/* Met a jour les positions */
		voice_ptr->spl.set_playback_pos (pos_int, pos_frac);
	}
}



void	Player::cmd_do_tone_porta (TrackInfo &track_info, int speed)
{
	float		new_period;

	/* Vitesse du Tone Portamento */
	if (speed == 0)
	{
		speed = track_info.score.porta.speed;
	}
	track_info.score.porta.speed = speed;

	/* Reglage de la nouvelle periode */
	new_period = track_info.score.period;
	if (track_info.score.porta.period < new_period)
	{
		new_period -= speed;
		new_period = MAX (new_period, track_info.score.porta.period);
	}
	else if (track_info.score.porta.period > new_period)
	{
		new_period += speed;
		new_period = MIN (new_period, track_info.score.porta.period);
	}

	/* Validation de la periode */
	set_period (track_info, new_period);
}



void	Player::cmd_do_vibrato (TrackInfo &track_info, int param)
{
	float		new_period;
	int		speed;
	int		amplitude;

	/* Vitesse du Vibrato */
	speed = (param & 0xF0) >> 2;
	if (speed == 0)
	{
		speed = track_info.score.vibrato.speed;
	}
	track_info.score.vibrato.speed = speed;

	/* Amplitude du Vibrato */
	amplitude = param & 0xF;
	if (amplitude == 0)
	{
		amplitude = track_info.score.vibrato.amp;
	}
	track_info.score.vibrato.amp = amplitude;

	/* Reglage de la nouvelle periode */
	new_period =   track_info.score.period
					 + ((  Player::_sin_table [track_info.score.vibrato.waveform]
	                                      [(track_info.score.vibrato.cnt >> 2) & 0x3F]
						  * amplitude) / 8.0f);

	/* Validation de la periode locale */
	new_period = MIN (new_period, Player::MAX_PERIOD);
	new_period = MAX (new_period, Player::MIN_PERIOD);
	track_info.score.period_loc = new_period;

	/* Compteur suivant */
	track_info.score.vibrato.cnt += speed;
}



void	Player::cmd_do_lin_vol_slide_up (TrackInfo &track_info, int speed)
{
	if (speed == 0)
	{
		speed = track_info.score.vol_slide.speed;
	}
	track_info.score.vol_slide.speed = speed;
	set_lin_velocity (track_info, track_info.score.velocity_lin + speed * 8);
}



void	Player::cmd_do_lin_vol_slide_down (TrackInfo &track_info, int speed)
{
	if (speed == 0)
	{
		speed = track_info.score.vol_slide.speed;
	}
	track_info.score.vol_slide.speed = speed;
	set_lin_velocity (track_info, track_info.score.velocity_lin - speed * 8);
}


void	Player::cmd_do_pan_slide_left (TrackInfo &track_info, long speed)
{
	if (speed == 0)
	{
		speed = track_info.score.pan_slide.speed;
	}
	track_info.score.pan_slide.speed = (UWORD) speed;

	set_panning (track_info, false, (long)track_info.score.track.panning - speed);
}



void	Player::cmd_do_pan_slide_right (TrackInfo &track_info, long speed)
{
	if (speed == 0)
	{
		speed = track_info.score.pan_slide.speed;
	}
	track_info.score.pan_slide.speed = (UWORD) speed;

	set_panning (track_info, false, (long)track_info.score.track.panning + speed);
}



void	Player::cmd_do_set_mix_preset (TrackInfo &track_info, int preset, int flags)
{
	int		dest_track_type;
	int		dest_track_nbr;

	if (preset <= 0 || preset > MIXP_NBRMIXP_MAXI)
	{
		return;
	}

	/* Sur une piste d'effet, on applique le preset a la
	   piste qui a envoye la commande. */
	if (track_info.track_type == Pattern_TYPE_FX)
	{
		dest_track_type = track_info.track_type;
		dest_track_nbr = track_info.track;
	}

	/* Sinon on utilise la piste destination par defaut du preset. */
	else
	{
		dest_track_type = MIXP_get_track_type (preset);
		dest_track_nbr = MIXP_get_track_number (preset);
	}

	/* On ne verifie pas si l'action a reussi ou pas,
	   de toute facon on s'en fout. */
	load_mix_preset (dest_track_type, dest_track_nbr, preset, flags);
}



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



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