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

        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.

*Tab=3***********************************************************************/



#if defined (_MSC_VER)
	#pragma warning (1 : 4130 4223 4705 4706)
	#pragma warning (4 : 4355 4786 4800)
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "archi.h"
#include "base.h"
#include "base_ct.h"
#include	"config.h"
#include	"ConfigKey.h"
#include	"ConfigKeyValue.h"
#include "d2d.h"
#include "gtracker.h"
#include "inst.h"
#include	"List.h"
#include	"log.h"
#include "mixer.h"
#include "os.h"
#include "Player.h"
#include "samp.h"
#include "Sample.h"
#include	"SimpleCalculator.h"
#include "song.h"
#include	"splhandl.h"
#include	"String.h"
#include	"WaveForm.h"

#include	<algorithm>

#include	<cassert>
#include	<cstdio>
#include <cmath>



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



Player &	Player::use_instance ()
{
	static Player	_instance;

	return (_instance);
}



/*==========================================================================*/
/*      Nom: read_conf                                                      */
/*      Description: Lit les informations concernant le player dans le      */
/*                   fichier de configuration.                              */
/*      Parametres en entree:                                               */
/*        key_list: la liste des cles correspondant a la cle "/player" dans */
/*                  le fichier de configuration.                            */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

int	Player::read_conf (const std::list <ConfigKey> &key_list)
{
	int				int_val;

	std::list <ConfigKey>::const_iterator	it = key_list.begin ();

	while (it != key_list.end ())
	{
		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		const String	key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();

		/* Cle anonyme: interdit */
		if (BASE_compare_string (key_name.c_str (), "") == 0)
		{
			LOG_printf ("read_conf: Warning: (in \"module_editing\") unexpected unassigned value.\n");
		}

		/* Latency */
		else if (BASE_compare_string (key_name.c_str (), "latency") == 0)
		{
			if (CFG_get_int (key_val, "player/latency", int_val))
			{
				int_val = MAX (int_val, 0);
				if (OS_sound_driver.set_latency (int_val))
				{
					LOG_printf ("read_conf: Warning: (in \"module_editing/latency\") bad parameter value.\n");
				}
			}
		}

		/* Cle inconnue */
		else
		{
			LOG_printf ("read_conf: Warning: (in \"module_editing\") syntax error \"%s\".\n",
			            key_name.c_str ());
		}

		++ it;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: first_init                                                     */
/*      Description: Initialise les differentes tables utiles pour le       */
/*                   player, ainsi que la zone de cache de D2D.             */
/*      Retour: 0 si tout va bien.                                          */
/*==========================================================================*/

int	Player::first_init ()
{
	LOG_printf ("first_init:\n");

	if (fx_init ())
	{
		LOG_printf ("first_init: Error: couldn't initialize effect subroutine jump tables.\n");
		return (-1);	/* Erreur lors de l'init des variables des effets */
	}

	LOG_printf ("\tOK.\n");

	return (0);
}



void	Player::first_restore ()
{
	fx_restore ();
}



/*==========================================================================*/
/*      Nom: second_init                                                    */
/*      Description: Demarre le player (routine sous interruption).         */
/*                   Le driver doit deja etre installe.                     */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

int	Player::second_init ()
{
	double	tick_len;
	int		nbr_frames;
	int		track;
	int		track_type;
	int		global_track_nbr;

	LOG_printf ("second_init:\n");

	tick_len = (double)(MIX_replay_freq << 1) / ((LWORD)Player::INIT_TEMPO * 5);
	nbr_frames = (int) ceil ((tick_len + 1) / MIX_MAX_FRAME_LENGTH);

	/* Initialisation de la stucture de la song */
	_score_info.tick_len  = int64_t (tick_len * 4294967296.0 + 0.5);
	_score_info.frame_pos = _score_info.tick_len;
	_score_info.frame_len = (LWORD) (tick_len / nbr_frames);

	_score_info.tempo = Player::INIT_TEMPO;
	_score_info.time_high = Player::INIT_TIME_HIGH;
	_score_info.time_low = Player::INIT_TIME_LOW;

	_score_info.tick_counter = 0;
	_score_info.speed = Player::INIT_SPEED;
	_score_info.pattern_delay = 0;
	_score_info.current_line = 0;
	_score_info.current_pos = 0;
	_score_info.next_line = 0;
	_score_info.next_pos = 0;
	_score_info.next_flag = false;
	_score_info.linear_period_flag = Player::INIT_LINEAR_PERIOD_FLAG;

	_score_info.current_pattern = 0;

	_score_info.current_play_mode = Player::MODE_STOP;

	_elapsed_time = 0;

	/* Affectation des numeros de piste absolus */
	global_track_nbr = 0;
	for (track_type = 0; track_type < GTK_NBR_MIX_TRACK_TYPE; track_type ++)
	{
		for (track = 0; track < GTK_nbr_tracks [track_type]; track ++)
		{
			_track_info_list [track_type] [track] = global_track_nbr;
			TrackInfo	&	track_info = _track_info [global_track_nbr];
			track_info.track = track;
			track_info.track_type = track_type;
			set_track_name (track_type, track, "");
			global_track_nbr ++;
		}
	}

	reset_mix_parameters ();

	if (set_replay_format ())
	{
		LOG_printf ("second_init: Error: couldn't set replay format.\n");
		return (-1);
	}

	if (start_player ())
	{
		LOG_printf ("second_init: Error: couldn't start player.\n");
		return (-1);
	}

	LOG_printf ("\tOK.\n");

	return (0);
}



void	Player::second_restore ()
{
	if (_player_started_flag)
	{
		set_play_mode (Player::MODE_STOP_ALL, -1, -1, false);
		clear_all_effects ();
		stop_player ();
	}
}



/*==========================================================================*/
/*      Nom: start_player                                                   */
/*      Description: Demarre le player, une fois que toutes les donnees     */
/*                   necessaires ont ete initialisees.                      */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

int	Player::start_player ()
{
	if (! _player_started_flag)
	{
		/* Passe en mode Stop pour demarrer */
		_new_play_mode = Player::MODE_STOP_ALL;
		_new_play_mode_flag = true;

		_player_thread_state = 0;
		if (_player_thread.spawn ())
		{
			LOG_printf ("start_player: Error: couldn't spawn player thread.\n");
			return (-1);
		}

		/* Attend que le thread ait fini son initialisation */
		while (_player_thread_state == 0)
		{
			Thread::sleep (10);
		}

		if (_player_thread_state < 0)
		{
			LOG_printf ("start_player: Error: Problem in player thread.\n");
			while (_player_thread.is_running ())
			{
				Thread::sleep (10);
			}
			_player_thread_state = 0;
			return (-1);
		}

		_player_started_flag = true;

		/* On se fixe un niveau de priorite en-dessous de la normale. */
		if (OS_main_thread.set_priority (Thread_PRIORITY_BELOW_NORMAL))
		{
			LOG_printf ("start_player: Warning: couldn't set Idle Priority for main thread.\n");
		}
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: stop_player                                                    */
/*      Description: Arrete le player, qui peut etre remis en marche plus   */
/*                   tard.                                                  */
/*==========================================================================*/

void	Player::stop_player ()
{
	if (_player_started_flag)
	{
		set_play_mode (Player::MODE_STOP_ALL, -1, -1, false);

		/* Arrete le thread du player */
		_player_thread.post_message (Thread_MESSAGE_QUIT);
		while (_player_thread.is_running ())
		{
			Thread::sleep (10);
		}

		_player_started_flag = false;

		/* On revient a un niveau de priorite normal */
		if (OS_main_thread.set_priority (Thread_PRIORITY_NORMAL))
		{
			LOG_printf ("stop_player: Warning: couldn't set Normal Priority for main thread.\n");
		}
	}
}



/*==========================================================================*/
/*      Nom: get_player_started_flag                                        */
/*      Description: Indique si le player est en marche ou pas.             */
/*      Retour: true si le player joue, false sinon.                        */
/*==========================================================================*/

bool	Player::get_player_started_flag ()
{
	return (_player_started_flag);
}



/*==========================================================================*/
/*      Nom: add_new_track                                                  */
/*      Description: Ajoute de nouvelles pistes d'un type donne dans le     */
/*                   tableau des numeros de pistes absolus. Aucune modif    */
/*                   n'est faite au niveau de la partition, mais les        */
/*                   donnees des pistes sont initialisees. Cette routine    */
/*                   doit etre appelee avant de modifier GTK_nbr_tracks []. */
/*                   GTK_mutex must be locked beforehand.                   */
/*      Parametres en entree:                                               */
/*        - track_type: type des pistes a rajouter.                         */
/*        - nbr_tracks: nombre de pistes a rajouter. Ce nombre peut etre    */
/*                      negatif si des pistes sont enlevees.                */
/*==========================================================================*/

void	Player::add_new_track (int track_type, int nbr_tracks)
{
	int		global_track_nbr;
	int		track_type_2;
	int		track;
	int		nbr_tracks_2;
	int	track_count;
	bool		free_flag;

/*______________________________________________
 *
 * On retire des pistes
 *______________________________________________
 */

	if (nbr_tracks <= 0)
	{
		/* Pour toutes les pistes, il faut virer les voix */
		{
			for (track_count = nbr_tracks; track_count < 0; track_count ++)
			{
				global_track_nbr = _track_info_list [track_type]
				                                    [GTK_nbr_tracks [track_type] + track_count];
				TrackInfo &		track_info = _track_info [global_track_nbr];

				for (int voice_cnt = 0; voice_cnt < track_info.mix.spl.nbr_voices; ++voice_cnt)
				{
					const int		voice_index = track_info.mix.spl.voice_index_arr [voice_cnt];
					deallocate_voice (voice_index);
					track_info.mix.spl.voice_index_arr [voice_cnt] = -1;

					Voice &			voice = _voice_arr [voice_index];

					/* Buffers de D2D */
					voice.spl.d2d._desc.deallocate ();
				}
				track_info.mix.spl.nbr_voices = 0;
			}
		}

		/* Pour les pistes d'effets, il faut virer proprement l'effet */
		if  (track_type == Pattern_TYPE_FX)
		{
			for (track_count = nbr_tracks; track_count < 0; track_count ++)
			{
				global_track_nbr = _track_info_list [Pattern_TYPE_FX]
				                                        [GTK_nbr_tracks [Pattern_TYPE_FX] + track_count];
				Player::TrackInfo &	track_info = _track_info [global_track_nbr];

				if (   track_info.mix.fx.effect != FxPreset_TYPE_NONE
				    && ! track_info.mix.fx.bad_init_flag)
				{
					fx_fx_restore (track_info);
				}
			}
		}

		/* Il faut egalement virer ces pistes des presets de mixage,
			voire carrement virer les presets. */
		for (track_count = nbr_tracks; track_count < 0; track_count ++)
		{
			track = GTK_nbr_tracks [track_type] + track_count;
			MIXP_remove_track_from_all_presets (track_type, track);
		}

		return;
	}

/*______________________________________________
 *
 * On ajoute des pistes
 *______________________________________________
 */

	global_track_nbr = 0;

	for (track_count = 0; track_count < nbr_tracks; track_count ++)
	{
		/* Recherche un numero global de piste inutilise */
		for (;;)
		{
			free_flag = true;
			for (track_type_2 = 0; track_type_2 < GTK_NBR_MIX_TRACK_TYPE; track_type_2 ++)
			{
				nbr_tracks_2 = GTK_nbr_tracks [track_type_2];

				for (track = 0; track < nbr_tracks_2; track ++)
				{
					if (_track_info_list [track_type_2] [track] == global_track_nbr)
					{
						free_flag = false;
						break;
					}
				}

				if (! free_flag)
				{
					break;
				}
			}

			if (free_flag)
			{
				break;
			}

			global_track_nbr ++;
		}

		/* Affecte le numero a la piste */
		track = GTK_nbr_tracks [track_type] + track_count;
		_track_info_list [track_type] [track] = global_track_nbr;

		/* Initialise les info */
		TrackInfo &		track_info = _track_info [global_track_nbr];
		track_info.track = track;
		track_info.track_type = track_type;
		track_info.outvol = 0x1000;
		track_info.outpan = 0x8000U;
		track_info.dry_outvol = 0x1000;
		track_info.dry_outpan = 0x8000U;
		track_info.wet_mute_flag = false;
		track_info.dry_mute_flag = false;
		set_track_name (track_type, track, "");
		set_lin_volume (track_info, 0x1000);
		set_dry_lin_volume (track_info, 0x1000);
		track_info.meter.set_sample_freq (MIX_replay_freq);
		track_info.meter.clear_buffers ();

		track_info.stereo = 1;

		{
			// Allocates a voice
			const int		voice_index = allocate_voice ();

			track_info.mix.spl.voice_index_arr [0] = voice_index;
			track_info.mix.spl.nbr_voices = 1;

			Voice &			voice = _voice_arr [voice_index];
			voice.spl.set_hq_interpolator (
				MIX_get_sound_quality () > MIX_SOUND_QUALITY_NORMAL
			);
			voice.deactivate ();
		}

		switch (track_type)
		{
		case	Pattern_TYPE_SPL:
			// Nothing more
			break;

		case	Pattern_TYPE_AIN:
			/*** A voir ***/
			track_info.stereo = 2;
			break;

		case	Pattern_TYPE_FX:
			track_info.stereo = 1;
			track_info.mix.fx.old_effect = FxPreset_TYPE_NONE;
			track_info.mix.fx.effect = FxPreset_TYPE_NONE;
			track_info.mix.fx.bypass_flag = false;
			track_info.mix.fx.update_flag = false;
			track_info.mix.fx.bad_init_flag = false;

			track_info.mix.src.nbr_s_tracks = 1;
			track_info.mix.src.track_conf [0].conf.track_type = Pattern_TYPE_SPL;
			track_info.mix.src.track_conf [0].conf.wet_flag = true;
			track_info.mix.src.track_conf [0].conf.cr.old_vol [0] = 0;
			track_info.mix.src.track_conf [0].conf.cr.old_vol [1] = 0;
			if (track < GTK_nbr_tracks [Pattern_TYPE_FX])
			{
				track_info.mix.src.track_conf [0].conf.track_nbr = track;
			}
			else
			{
				track_info.mix.src.track_conf [0].conf.track_nbr = 0;
			}
			track_info.mix.src.track_conf [0].conf.inpvol = 0x1000;
			track_info.mix.src.track_conf [0].conf.inppan = 0x8000U;
			track_info.mix.src.track_conf [0].meter.set_sample_freq (MIX_replay_freq);
			track_info.mix.src.track_conf [0].meter.clear_buffers ();
			break;

		case	Pattern_TYPE_MID:

			/*** A faire ***/

			break;
		}
		reset_track_partition_info (track_info);
		track_info.score.track.panning = 0x8000U;
		track_info.score.track.drypan = 0x8000U;

		global_track_nbr ++;
	}
}



/*==========================================================================*/
/*      Nom: set_replay_freq                                                */
/*      Description: Fixe une nouvelle frequence de replay. Ne doit etre    */
/*                   appele que quand la deuxieme initialisation est        */
/*                   terminee.                                              */
/*      Parametres en entree:                                               */
/*        - freq: la nouvelle frequence, en Hz.                             */
/*==========================================================================*/

void	Player::set_replay_freq (long freq)
{
	int		track;

	std::lock_guard <std::mutex>	lock (GTK_mutex);

	MIX_replay_freq = freq;

	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_FX]; track ++)
	{
		Player::TrackInfo &	track_info =
			_track_info [_track_info_list [Pattern_TYPE_FX] [track]];
		fx_fx_silence (track_info);
		fx_fx_update (track_info);
	}

	SimpleCalculator::set_spec_var (SimpleCalculator::SAMPLE_FREQ,
	                                static_cast <double> (freq));

	_elapsed_time = 0;
}



/*==========================================================================*/
/*      Nom: main_interrupt                                                 */
/*      Description: Routine appelee par l'interruption du driver de la     */
/*                   carte son. Elle doit etre placee avant la routine de   */
/*                   mixage car elle definit la nouvelle longueur de la     */
/*                   frame.                                                 */
/*==========================================================================*/

void	Player::main_interrupt ()
{
	/* On s'isole */
	std::lock_guard <std::mutex>	lock (GTK_mutex);

/*______________________________________________
 *
 * Changement de frame
 *______________________________________________
 */

	// Tick finished, starts a new one
	if (_score_info.frame_pos >= _score_info.tick_len)
	{
		_score_info.frame_pos -= _score_info.tick_len;

		handle_score ();

		// Updates the tick duration
		const double   tick_len_dbl =
			MIX_replay_freq * (60.0 / 4 / 6) / _score_info.tempo;
		_score_info.tick_len = int64_t (tick_len_dbl * 4294967296.0 + 0.5);
	}

	// Updates the frame length
	const int      rem_len =
		(_score_info.tick_len - _score_info.frame_pos + 0xFFFFFFFFU) >> 32;
	_score_info.frame_len = std::min (rem_len, MIX_MAX_FRAME_LENGTH);

	// Terminates the frame
	_score_info.frame_pos += int64_t (_score_info.frame_len) << 32;

	// Clock
	if (_score_info.current_play_mode != MODE_STOP)
	{
		_elapsed_time += _score_info.frame_len;
	}

/*______________________________________________
 *
 * Prise en compte des nouvelles donnees
 * introduites par le tracker
 *______________________________________________
 */

	if (_new_line_flag [Pattern_TYPE_SPL] != 0)
	{
		play_pattern_line_spl ();
	}

	if (_new_line_flag [Pattern_TYPE_AIN] != 0)
	{
		play_pattern_line_ain ();
	}

	if (_new_line_flag [Pattern_TYPE_FX] != 0)
	{
		play_pattern_line_fx ();
	}

	if (_new_line_flag [Pattern_TYPE_MID] != 0)
	{
		play_pattern_line_mid ();
	}

	/* Nouvelle longueur officielle */
	MIX_frame_length = _score_info.frame_len;

	/* On peut autoriser quelqu'un d'autre a utiliser les donees */
}



/*==========================================================================*/
/*      Nom: handle_score                                                   */
/*      Description: Gestion generale de la partition.                      */
/*==========================================================================*/

void	Player::handle_score ()
{
	_score_info.tick_counter ++;

	/* Changement de mode de replay */
	if (_new_play_mode_flag)
	{
		if (   ! _new_play_mode_cont_flag
			 || _score_info.current_play_mode == MODE_STOP)
		{
			_score_info.tick_counter = _score_info.speed;
			_score_info.pattern_delay = 0;
		}

		/* Recupere ligne et position courante */
		if (_score_info.current_play_mode != MODE_STOP)
		{
			_current_linepos = _score_info.current_line;
		}
		if (_score_info.current_play_mode == MODE_SONG)
		{
			_current_songpos = _score_info.current_pos;
		}

		/* Met a jour ces positions avec celles demandees */
		if (_new_songpos >= 0)
		{
			_current_songpos = _new_songpos;
		}
		if (   _new_linepos >= 0
			 && _new_play_mode_cont_flag)
		{
			_current_linepos = _new_linepos;
		}

		switch (_new_play_mode)
		{
		/* Stop/Direct */
		case	MODE_STOP_ALL:
		case	MODE_STOP:
			if (_new_play_mode == MODE_STOP_ALL)
			{
				_score_info.frame_pos = 0;
				reset_all_tracks ();
			}
			_score_info.tick_counter = _score_info.speed;
			_score_info.pattern_delay = 0;
			_score_info.next_pos = 0;
			_score_info.next_line = 0;
			_score_info.next_flag = true;
			_score_info.current_play_mode = MODE_STOP;
			break;

		/* Play Song */
		case	Player::MODE_SONG:
			if (_score_info.current_play_mode == MODE_STOP)
			{
				_score_info.next_pos = _current_songpos;
				_score_info.next_line = _current_linepos;
				_score_info.next_flag = true;
			}
			else
			{
				_score_info.current_pos = _current_songpos;
				_score_info.current_pattern = SONG_get_pattern_number (_current_songpos);
				_score_info.current_line = _current_linepos;
			}

			if (! _new_play_mode_cont_flag)
			{
				_score_info.next_pos = _current_songpos;
				_score_info.next_line = 0;
				_score_info.next_flag = true;
			}

			_score_info.current_play_mode = Player::MODE_SONG;
			_elapsed_time = 0;
			break;

		/* Play_pattern */
		case	MODE_PATTERN:
			_score_info.current_pattern = SONG_get_pattern_number (_current_songpos);

			if (_score_info.current_play_mode == MODE_STOP)
			{
				_score_info.next_pos = _current_songpos;
				_score_info.next_line = _current_linepos;
				_score_info.next_flag = true;
			}
			else
			{
				_score_info.current_pos = _current_songpos;
				_score_info.current_line = _current_linepos;
			}

			if (! _new_play_mode_cont_flag)
			{
				_score_info.next_pos = _current_songpos;
				_score_info.next_line = 0;
				_score_info.next_flag = true;
			}
			_score_info.current_play_mode = MODE_PATTERN;
			_elapsed_time = 0;
			break;
		}

		_new_play_mode_flag = false;
	}

	/* Passage a la ligne suivante */
	if (   _score_info.tick_counter
		 >= _score_info.speed * (_score_info.pattern_delay + 1))
	{
		_score_info.tick_counter = 0;
		_score_info.pattern_delay = 0;

		/* Deroulement devie de sa trajectoire habituelle */
		if (_score_info.next_flag)
		{
			_score_info.current_line = _score_info.next_line;
			_score_info.next_flag = false;
			_score_info.next_line = 0;
			_score_info.current_pos = _score_info.next_pos;
			switch (_score_info.current_play_mode)
			{
			case	MODE_SONG:
			case	MODE_PATTERN:
				_score_info.current_pattern = SONG_get_pattern_number (_score_info.current_pos);
				break;
			case	MODE_STOP:
				if (_score_info.current_pos <= 0)
				{
					_score_info.current_pattern = GTK_PATTERN_BIDON_A;
				}
				else
				{
					_score_info.current_pattern = GTK_PATTERN_BIDON_B;
				}
				break;
			}
		}

		/* Ligne suivante dans la partition */
		else
		{
			_score_info.current_line ++;
		}

		/* Teste si on est toujours dans le pattern et la song */
		if (_score_info.current_line >= PAT_get_pattern_height (_score_info.current_pattern))
		{
			_score_info.current_line = 0;
			_score_info.current_pos ++;

			switch (_score_info.current_play_mode)
			{
			case	MODE_SONG:
				if (_score_info.current_pos >= SONG_get_song_length ())
				{
					_score_info.current_pos = SONG_get_song_repeat ();
				}
				_score_info.current_pattern = SONG_get_pattern_number (_score_info.current_pos);
				break;
			case	MODE_STOP:
				if (_score_info.current_pos <= 0)
				{
					_score_info.current_pattern = GTK_PATTERN_BIDON_A;
				}
				else
				{
					_score_info.current_pattern = GTK_PATTERN_BIDON_B;
				}
				break;
			}
		}

		/* Recopie toute la ligne pointee par current_line dans les info des pistes */
		copy_line_to_track_info ();
	}

	if (_score_info.tick_counter == 0)
	{
		handle_effects_1st_tick ();
	}
	handle_effects_all_ticks ();
	env_manage_all_envelopes ();
	compute_final_parameters ();

	kill_inactive_ghost_voices ();
}



/*==========================================================================*/
/*      Nom: set_new_note                                                   */
/*      Description: Declanche une note de la partition.                    */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: la piste de samples sur laquelle                    */
/*                          la nouvelle note doit etre prise en compte.     */
/*==========================================================================*/

void	Player::set_new_note (Player::TrackInfo &track_info)
{
	unsigned int	effect;
	unsigned int	effect_nbr;
	int		instr;
	int		note;
	int		current_note;
	int		volume;
	int		played_note;
	int		sample;
	int		autobal;

	note = track_info.score.score.note;
	instr = track_info.score.score.instr;
	effect = track_info.score.score.effect;
	effect_nbr = effect >> 8;

/*______________________________________________
 *
 * Changement d'instrument
 *______________________________________________
 */

	if (instr != 0)
	{
		track_info.score.instr = instr;
		Player::set_lin_velocity (track_info, INST_get_instr_volume (instr) * 8);
		current_note = track_info.score.note;
		sample = INST_get_instr_sample (instr, current_note);
		track_info.score.sample.number = sample;
		track_info.score.sample.transp = INST_get_instr_transp (instr, current_note);
		track_info.score.sample.volume = SAMP_get_sample_volume (sample);
		track_info.score.sample.finetune = SAMP_get_sample_finetune (sample);
		autobal = SAMP_get_sample_balance (sample);
		if (autobal != -1)
		{
			track_info.score.track.panning = (UWORD)autobal << 4;
		}

		init_filter (track_info);
	}

/*______________________________________________
 *
 * Note
 *______________________________________________
 */

	if (note != 0)
	{
		/* Avec Tone Portamento */
		if (   effect_nbr == 0x03
			 || effect_nbr == 0x05
			 || effect_nbr == 0x06
			 || effect_nbr == 0xAB
			 || (effect_nbr >= 0x18 && effect_nbr <= 0x1B))
		{
			track_info.score.note = note;
			track_info.score.porta.note = note;
			played_note = note + (int)track_info.score.sample.transp;
			played_note = MIN (played_note, GTK_NBRNOTES_MAXI-1);
			played_note = MAX (played_note, 1);
			track_info.score.porta.period =
				(float) convert_note_to_internal_period
				(played_note, track_info.score.sample.finetune);
		}

		/* Sans Tone Portamento */
		else
		{
			track_info.score.note = note;
			track_info.score.porta.note = note;
			instr = track_info.score.instr;

			int				voice_index = track_info.mix.spl.voice_index_arr [0];
			Voice *			voice_ptr = &(_voice_arr [voice_index]);

			if (instr != 0)
			{
				sample = INST_get_instr_sample (instr, note);
				track_info.score.sample.number = sample;
				track_info.score.sample.transp = INST_get_instr_transp (instr, note);
				track_info.score.sample.volume = SAMP_get_sample_volume (sample);
				track_info.score.sample.finetune = SAMP_get_sample_finetune (sample);
				autobal = SAMP_get_sample_balance (sample);
				if (autobal != -1)
				{
					track_info.score.track.panning = (UWORD)autobal << 4;
				}

				if (track_info.track_type == Pattern_TYPE_SPL)
				{
					track_info.stereo = SAMP_get_sample_stereo (sample);

					voice_index = cut_note (track_info);
					voice_ptr = &(_voice_arr [voice_index]);
					voice_ptr->start_sample (sample);
				}
			}	/* Fin de instr != 0 */

			voice_ptr->_env_set.init_all_env (instr, track_info.track_type);

			/* Fixe la note maintenant */
			played_note = note + (int)track_info.score.sample.transp;
			played_note = MIN (played_note, GTK_NBRNOTES_MAXI - 1);
			played_note = MAX (played_note, 1);
			track_info.score.period =
				(float) convert_note_to_internal_period
				(played_note, track_info.score.sample.finetune);
			track_info.score.porta.period = track_info.score.period;
			track_info.score.porta.note = note;
		}	// Fin de Sans Tone portamento
	}	// Fin du if note != 0

/*______________________________________________
 *
 * Colonne de volume
 *______________________________________________
 */

	volume = track_info.score.score.volume;
	if (   volume >= 0x10
	    && volume <= 0x50)
	{
		Player::set_lin_velocity (track_info, (volume - 0x10) << 5);
	}
}



/*==========================================================================*/
/*      Nom: set_lin_velocity                                               */
/*      Description: Fixe la velocite lineaire d'une piste (et recalcule la */
/*                   velocite logarithmique).                               */
/*      Parametres en entree:                                               */
/*        - velo: La nouvelle velocite de la piste.                         */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::set_lin_velocity (Player::TrackInfo &track_info, int velo)
{
	velo = MIN (velo, 0x800);
	velo = MAX (velo, 0);
	track_info.score.velocity_lin = velo;
	track_info.score.velocity_loc = velo;
	track_info.score.velocity_log = _instr_vol_table_log [velo];
	track_info.score.changed_volume_flag = true;
}



/*==========================================================================*/
/*      Nom: set_log_velocity                                               */
/*      Description: Fixe la velocite logarithmique d'une piste (et         */
/*                   recalcule la velocite lineaire).                       */
/*      Parametres en entree:                                               */
/*        - velo: La nouvelle velocite de la piste.                         */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::set_log_velocity (Player::TrackInfo &track_info, int velo)
{
	velo = MIN (velo, 0x800);
	velo = MAX (velo, 0);
	track_info.score.velocity_log = velo;
	track_info.score.velocity_lin = _instr_vol_table_exp [velo];
	track_info.score.velocity_loc = track_info.score.velocity_lin;
	track_info.score.changed_volume_flag = true;
}



/*==========================================================================*/
/*      Nom: set_lin_volume                                                 */
/*      Description: Fixe le volume lineaire d'une piste (et                */
/*                   recalcule le volume logarithmique).                    */
/*      Parametres en entree:                                               */
/*        - lin_vol: Le nouveau volume de la piste.                         */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::set_lin_volume (Player::TrackInfo &track_info, long lin_vol)
{
	int		log_vol;

	lin_vol = MIN (lin_vol, 0xFFFFL);
	lin_vol = MAX (lin_vol, 0);
	track_info.score.track.volume_lin = (UWORD)lin_vol;
	log_vol = lin_to_log_volume (lin_vol);
	track_info.score.track.volume_log = (UWORD)log_vol;
	if (   track_info.track_type == Pattern_TYPE_SPL
	    || track_info.track_type == Pattern_TYPE_AIN
		 || track_info.track_type == Pattern_TYPE_FX)
	{
		track_info.score.changed_volume_flag = true;
	}
	else
	{
		track_info.outvol = (UWORD)lin_vol;
	}
}



/*==========================================================================*/
/*      Nom: set_log_volume                                                 */
/*      Description: Fixe le volume logarithmique d'une piste (et           */
/*                   recalcule le volume lineaire).                         */
/*      Parametres en entree:                                               */
/*        - log_vol: Le nouveau volume de la piste.                         */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::set_log_volume (Player::TrackInfo &track_info, int log_vol)
{
	unsigned int	lin_vol;

	log_vol = MIN (log_vol, 0xFFF);
	log_vol = MAX (log_vol, 0);
	track_info.score.track.volume_log = (UWORD)log_vol;
	lin_vol = log_to_lin_volume (log_vol);
	track_info.score.track.volume_lin = (UWORD)lin_vol;
	if (   track_info.track_type == Pattern_TYPE_SPL
	    || track_info.track_type == Pattern_TYPE_AIN
		 || track_info.track_type == Pattern_TYPE_FX)
	{
		track_info.score.changed_volume_flag = true;
	}
	else
	{
		track_info.outvol = (UWORD)lin_vol;
	}
}



/*==========================================================================*/
/*      Nom: set_dry_lin_volume                                             */
/*      Description: Fixe le volume lineaire dry d'une piste (et            */
/*                   recalcule le volume logarithmique).                    */
/*      Parametres en entree:                                               */
/*        - lin_vol: Le nouveau volume de la piste.                         */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::set_dry_lin_volume (Player::TrackInfo &track_info, long lin_vol)
{
	int		log_vol;

	lin_vol = MIN (lin_vol, 0xFFFFL);
	lin_vol = MAX (lin_vol, 0);
	track_info.score.track.dryvol_lin = (UWORD)lin_vol;
	log_vol = lin_to_log_volume (lin_vol);
	track_info.score.track.dryvol_log = (UWORD)log_vol;
	if (   track_info.track_type == Pattern_TYPE_SPL
	    || track_info.track_type == Pattern_TYPE_AIN
		 || track_info.track_type == Pattern_TYPE_FX)
	{
		track_info.score.changed_volume_flag = true;
	}
	else
	{
		track_info.dry_outvol = (UWORD)lin_vol;
	}
}



/*==========================================================================*/
/*      Nom: set_dry_log_volume                                             */
/*      Description: Fixe le volume logarithmique dry d'une piste (et       */
/*                   recalcule le volume lineaire).                         */
/*      Parametres en entree:                                               */
/*        - log_vol: Le nouveau volume de la piste.                         */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::set_dry_log_volume (Player::TrackInfo &track_info, int log_vol)
{
	unsigned int	lin_vol;

	log_vol = MIN (log_vol, 0xFFF);
	log_vol = MAX (log_vol, 0);
	track_info.score.track.dryvol_log = (UWORD)log_vol;
	lin_vol = log_to_lin_volume (log_vol);
	track_info.score.track.dryvol_lin = (UWORD)lin_vol;
	if (   track_info.track_type == Pattern_TYPE_SPL
	    || track_info.track_type == Pattern_TYPE_AIN
		 || track_info.track_type == Pattern_TYPE_FX)
	{
		track_info.score.changed_volume_flag = true;
	}
	else
	{
		track_info.dry_outvol = (UWORD)lin_vol;
	}
}



/*==========================================================================*/
/*      Nom: set_period                                                     */
/*      Description: Fixe la periode de la note pour une piste.             */
/*      Parametres en entree:                                               */
/*        - period: La nouvelle periode de la piste.                        */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::set_period (Player::TrackInfo &track_info, float period)
{
	period = MIN (period, Player::MAX_PERIOD);
	period = MAX (period, Player::MIN_PERIOD);
	track_info.score.period = period;
	track_info.score.period_loc = period;
}



/*==========================================================================*/
/*      Nom: set_panning                                                    */
/*      Description: Fixe le panning pour une piste.                        */
/*      Parametres en entree:                                               */
/*        - dry_flag: indique qu'on veut manipuler la partie Dry.           */
/*        - pan: La nouvelle valeur du panning.                             */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: les informations de la piste.                       */
/*==========================================================================*/

void	Player::set_panning (Player::TrackInfo &track_info, bool dry_flag, long pan)
{
	pan = MIN (pan, 0xFFFFL);
	pan = MAX (pan, 0);
	if (dry_flag)
	{
		track_info.score.track.drypan = (UWORD) pan;
	}
	else
	{
		track_info.score.track.panning = (UWORD) pan;
	}
}




/****************************************************************************/
/*                                                                          */
/*      INTERFACAGE AVEC L'EXTERIEUR                                        */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: get_play_mode                                                  */
/*      Description: Renvoie le mode de replay dans lequel on est.          */
/*      Retour: Le numero du mode.                                          */
/*==========================================================================*/

int	Player::get_play_mode () const
{
	return (_score_info.current_play_mode);
}



/*==========================================================================*/
/*      Nom: set_play_mode                                                  */
/*      Description: Change le mode de replay (Stop/Step, Song et Pattern). */
/*                   Le mode Stop/Step permet de stopper toute activite     */
/*                   sonore ou de jouer une ligne de pattern. Cette ligne   */
/*                   devra etre placee dans le(s) pattern(s) bidon(s).      */
/*                   La fonction rend la main une fois que le nouveau mode  */
/*                   a effectivement ete pris en compte. Le nouveau mode    */
/*                   est cense prendre effet au prochain tick.              */
/*      Parametres en entree:                                               */
/*        - play_mode: Mode de replay (0, 1 ou 2)                           */
/*        - position: position de debut de replay dans la song. N'est       */
/*                    utilise que par les modes 1 et 2.                     */
/*        - line: numero de ligne dans le pattern. N'est utilise que par    */
/*                les modes 1 et 2 avec cont_flag = true.                   */
/*        - cont_flag: Pour les modes song et pattern, true indique que le  */
/*                     replay commence (ou continue) exactement a la        */
/*                     position et ligne courante.                          */
/*==========================================================================*/

void	Player::set_play_mode (int play_mode, int position, int line, bool cont_flag)
{
	_new_play_mode = play_mode;
	_new_play_mode_cont_flag = cont_flag;
	_new_linepos = line;
	_new_songpos = position;
	_new_play_mode_flag = true;

	Player::wait_for_new_play_mode ();
}



/*==========================================================================*/
/*      Nom: wait_for_new_play_mode                                         */
/*      Description: Attend que le nouveau mode de replay ait ete pris en   */
/*                   compte.                                                */
/*==========================================================================*/

void	Player::wait_for_new_play_mode ()
{
	/* Si le player est en marche, attend que le nouveau
	   mode ait ete pris en compte. */
	if (get_player_started_flag ())
	{
		while (_new_play_mode_flag)
		{
			/* Boucle d'attente vide */
		}
	}
}



/*==========================================================================*/
/*      Nom: clear_all_effects                                              */
/*      Description: Retire tous les effets de toutes les pistes d'effet.   */
/*==========================================================================*/

void	Player::clear_all_effects ()
{
	std::lock_guard <std::mutex>	lock (GTK_mutex);

	for (int track = 0; track < GTK_nbr_tracks [Pattern_TYPE_FX]; track ++)
	{
		Player::TrackInfo &	track_info =
			_track_info [_track_info_list [Pattern_TYPE_FX] [track]];

		if (   track_info.mix.fx.effect != FxPreset_TYPE_NONE
		    && ! track_info.mix.fx.bad_init_flag)
		{
			fx_fx_restore (track_info);
		}
	}
}



/*==========================================================================*/
/*      Nom: silence_all_effect_buffers                                     */
/*      Description: Reduit au silence tous les buffers des effets.         */
/*==========================================================================*/

void	Player::silence_all_effect_buffers ()
{
	std::lock_guard <std::mutex>	lock (GTK_mutex);

	for (int track = 0; track < GTK_nbr_tracks [Pattern_TYPE_FX]; track ++)
	{
		Player::TrackInfo &	track_info =
			_track_info [_track_info_list [Pattern_TYPE_FX] [track]];

		if (   track_info.mix.fx.effect != FxPreset_TYPE_NONE
		    && ! track_info.mix.fx.bad_init_flag)
		{
			fx_fx_silence (track_info);
		}
	}
}


/*==========================================================================*/
/*      Nom: get_song_position                                              */
/*      Description: Renvoie la position courante dans la song.             */
/*      Retour: La position.                                                */
/*==========================================================================*/

int	Player::get_song_position () const
{
	if (_score_info.current_play_mode != Player::MODE_SONG)
	{
		return (_current_songpos);
	}

	return (_score_info.current_pos);
}



/*==========================================================================*/
/*      Nom: set_song_position                                              */
/*      Description: Fixe une nouvelle position dans la chanson.            */
/*      Parametres en entree:                                               */
/*        - songpos: La nouvelle position.                                  */
/*==========================================================================*/

void	Player::set_song_position (int songpos)
{
	if (_score_info.current_play_mode != Player::MODE_STOP)
	{
		Player::set_play_mode (_score_info.current_play_mode, songpos, -1, true);
		return;
	}

	_current_songpos = songpos;
}



/*==========================================================================*/
/*      Nom: get_line_position                                              */
/*      Description: Renvoie la ligne courante dans la pattern courant.     */
/*      Retour: La ligne.                                                   */
/*==========================================================================*/

int	Player::get_line_position () const
{
	if (_score_info.current_play_mode == Player::MODE_STOP)
	{
		return (_current_linepos);
	}

	return (_score_info.current_line);
}



/*==========================================================================*/
/*      Nom: set_line_position                                              */
/*      Description: Fixe une nouvelle ligne courante dans le pattern.      */
/*      Parametres en entree:                                               */
/*        - songpos: La nouvelle ligne.                                     */
/*==========================================================================*/

void	Player::set_line_position (int linepos)
{
	if (_score_info.current_play_mode != Player::MODE_STOP)
	{
		Player::set_play_mode (_score_info.current_play_mode, -1, linepos, true);
		return;
	}

	_current_linepos = linepos;
}



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

int	Player::get_current_tick () const
{
	return (_score_info.tick_counter);
}



void	Player::get_elapsed_time (int &minutes, int &seconds, int &miliseconds) const
{
	minutes     = 0;
	seconds     = 0;
	miliseconds = 0;

	const double		sample_freq = static_cast <double> (MIX_replay_freq);
	const double		elapsed_spl = static_cast <double> (_elapsed_time);

	if (elapsed_spl >= 0)
	{
		const double	elapsed_s_flt = elapsed_spl / sample_freq;
		const long		elapsed_ms =
			static_cast <long> (floor (elapsed_s_flt * 1000));

		const long		elapsed_s = elapsed_ms / 1000;
		miliseconds = elapsed_ms - elapsed_s * 1000;

		const long		elapsed_min = elapsed_s / 60;
		seconds = elapsed_s - elapsed_min * 60;

		minutes = elapsed_min;
	}
}



void	Player::reset_elapsed_time ()
{
	_elapsed_time = 0;
}



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

void	Player::reset_mix_parameters ()
{
	int		track;
	int		track_type;
	int		track_cnt;
	int		global_track_nbr;

	std::lock_guard <std::mutex>	lock (GTK_mutex);

	/* Initialisation des niveaux de clipping */
	set_max_clipping_level (0);
	for (track = 0; track < GTK_NBROUT_MAXI; track ++)
	{
		set_clipping_level (track, 0);
	}

	// Initialise voices, set them all as free
	const int		sound_quality = MIX_get_sound_quality ();
	const bool		hq_flag = (sound_quality > MIX_SOUND_QUALITY_NORMAL);
	_free_voice_stack.clear ();
	for (int voice_index = 0; voice_index < MAX_NBR_VOICES; ++voice_index)
	{
		Voice &			voice = _voice_arr [voice_index];

		voice.spl.set_hq_interpolator (hq_flag);

		voice.deactivate ();

		_free_voice_stack.push_back (voice_index);
	}

	// Allocates a single voice for all kinds of tracks
	for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
	{
		for (track = 0; track < GTK_nbr_tracks [track_type]; track ++)
		{
			global_track_nbr = _track_info_list [track_type] [track];
			TrackInfo	&	track_info = _track_info [global_track_nbr];

			track_info.mix.spl.voice_index_arr [0] = allocate_voice ();
			track_info.mix.spl.nbr_voices = 1;
		}
	}

	/* Initialisation des pistes de samples */
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; track ++)
	{
		global_track_nbr = _track_info_list [Pattern_TYPE_SPL] [track];
		TrackInfo	&	track_info = _track_info [global_track_nbr];
		set_lin_volume (track_info, 0x1000);
		track_info.score.track.panning = 0x8000U;
		track_info.stereo = 1;
		track_info.meter.set_sample_freq (MIX_replay_freq);
		track_info.meter.clear_buffers ();
	}

	/* Initialisation des pistes d'entree */
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_AIN]; track ++)
	{
		global_track_nbr = _track_info_list [Pattern_TYPE_AIN] [track];
		TrackInfo	&	track_info = _track_info [global_track_nbr];
		if (track >= 2)
		{
			track_info.stereo = 1;
		}
		else if (track == 1)
		{
			track_info.stereo = 0;
		}
		else
		{
			track_info.stereo = 2;
		}
		set_lin_volume (track_info, 0x1000);
		track_info.score.track.panning = 0x8000U;
		track_info.wet_mute_flag = false;
		track_info.dry_mute_flag = false;
		track_info.meter.set_sample_freq (MIX_replay_freq);
		track_info.meter.clear_buffers ();
	}

	/* Initialisation de la premiere piste de sortie: elle contient
		toutes les pistes de samples et d'effet en mode Wet. */
	global_track_nbr = _track_info_list [GTK_TRACK_TYPE_AOU] [0];
	TrackInfo	&	track_info = _track_info [global_track_nbr];
	track_info.stereo = 2;
	set_lin_volume (
		track_info,
	   (long) (0x2000 / (log (static_cast <double> (GTK_nbr_tracks [Pattern_TYPE_SPL])) / LN2 + 1))
	);
	track_info.mix.src.nbr_s_tracks = GTK_nbr_tracks [Pattern_TYPE_SPL];
	track_cnt = 0;
	for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
	{
		if (   track_type != Pattern_TYPE_SPL
		    && track_type != Pattern_TYPE_FX)
		{
			continue;
		}

		for (track = 0; track < GTK_nbr_tracks [track_type]; track ++)
		{
			track_info.mix.src.track_conf [track_cnt].conf.track_type = track_type;
			track_info.mix.src.track_conf [track_cnt].conf.wet_flag = true;
			track_info.mix.src.track_conf [track_cnt].conf.track_nbr = track;
			/* Ces deux lignes n'ont pas d'importance car les pistes Audio Out n'ont
				pas de reglage de volume ou de balance pour les pistes d'entree. */
			track_info.mix.src.track_conf [track_cnt].conf.inpvol = 0x1000;
			track_info.mix.src.track_conf [track_cnt].conf.inppan = 0x8000U;
			track_info.mix.src.track_conf [track_cnt].conf.cr.old_vol [0] = 0;
			track_info.mix.src.track_conf [track_cnt].conf.cr.old_vol [1] = 0;
			track_info.mix.src.track_conf [track_cnt].conf.cr._gain [0].set_initial_flag (true);
			track_info.mix.src.track_conf [track_cnt].conf.cr._gain [1].set_initial_flag (true);
			track_info.mix.src.track_conf [track_cnt].meter.set_sample_freq (MIX_replay_freq);
			track_info.mix.src.track_conf [track_cnt].meter.clear_buffers ();
			track_info.meter.set_sample_freq (MIX_replay_freq);
			track_info.meter.clear_buffers ();

			track_cnt ++;
		}
	}
	track_info.mix.src.nbr_s_tracks = track_cnt;

	/* Initialisation des autres pistes de sortie */
	for (track = 1; track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; track ++)
	{
		global_track_nbr = _track_info_list [GTK_TRACK_TYPE_AOU] [track];
		TrackInfo	&	track_info = _track_info [global_track_nbr];
		track_info.stereo = 1;
		set_lin_volume (track_info, 0x1000);
		track_info.mix.src.nbr_s_tracks = 0;
		track_info.mix.src.track_conf [track_cnt].meter.set_sample_freq (MIX_replay_freq);
		track_info.mix.src.track_conf [track_cnt].meter.clear_buffers ();
		track_info.meter.set_sample_freq (MIX_replay_freq);
		track_info.meter.clear_buffers ();
	}

	/* Initialisations des pistes d'effets: 1 piste de sample en entree. */
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_FX]; track ++)
	{
		global_track_nbr = _track_info_list [Pattern_TYPE_FX] [track];
		TrackInfo	&	track_info = _track_info [global_track_nbr];
		track_info.stereo = 1;
		set_dry_lin_volume (track_info, 0x1000);
		track_info.score.track.drypan = 0x8000U;
		set_lin_volume (track_info, 0x1000);
		track_info.score.track.panning = 0x8000U;
		track_info.wet_mute_flag = false;
		track_info.dry_mute_flag = false;
		track_info.mix.src.nbr_s_tracks = 1;

		track_info.mix.src.track_conf [0].conf.track_type = Pattern_TYPE_SPL;
		track_info.mix.src.track_conf [0].conf.wet_flag = true;
		if (track < GTK_nbr_tracks [Pattern_TYPE_SPL])
		{
			track_info.mix.src.track_conf [0].conf.track_nbr = track;
		}
		else
		{
			track_info.mix.src.track_conf [0].conf.track_nbr = 0;
		}
		track_info.mix.src.track_conf [0].conf.inpvol = 0x1000;
		track_info.mix.src.track_conf [0].conf.inppan = 0x8000U;
		track_info.mix.src.track_conf [0].conf.cr.old_vol [0] = 0;
		track_info.mix.src.track_conf [0].conf.cr.old_vol [1] = 0;
		track_info.mix.src.track_conf [0].conf.cr._gain [0].set_initial_flag (true);
		track_info.mix.src.track_conf [0].conf.cr._gain [1].set_initial_flag (true);
		track_info.mix.src.track_conf [0].meter.set_sample_freq (MIX_replay_freq);
		track_info.mix.src.track_conf [0].meter.clear_buffers ();
		track_info.meter.set_sample_freq (MIX_replay_freq);
		track_info.meter.clear_buffers ();
	}

	/* Initialisation des pistes MIDI */
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_FX]; track ++)
	{
		global_track_nbr = _track_info_list [Pattern_TYPE_FX] [track];
		TrackInfo	&	track_info = _track_info [global_track_nbr];

		/*** A faire ***/

	}

	_score_info.linear_period_flag = Player::INIT_LINEAR_PERIOD_FLAG;
	no_mute ();
	default_interpolation ();
}



/*==========================================================================*/
/*      Nom: add_source_track                                               */
/*      Description: Ajoute une piste source a une piste d'effet ou d'Audio */
/*                   Out. Les tests necessaires sont effectues pour savoir  */
/*                   si la nouvelle piste ne va pas introduire de bouclage, */
/*                   ou si elle est deja presente.                          */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste sur laquelle on va ajouter une     */
/*                      source.                                             */
/*        - track_nbr: Numero de la piste.                                  */
/*        - new_track_conf_ptr: pointeur sur la structure de configuration  */
/*                              de la piste.                                */
/*        - flags: permet de savoir de quelle facon  ajouter la piste       */
/*                 source (type MIX_PRESET_???). Tenter d'introduire        */
/*                 une piste deja existante en mode REPL n'a absolument     */
/*                 aucun effet.                                             */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 si la piste etait deja dans les sources (rien n'est       */
/*                change),                                                  */
/*              2 si l'introduction de la piste aurait induit un bouclage   */
/*                (operation annulee).                                      */
/*              -1 si erreur.                                               */
/*==========================================================================*/

int	Player::add_source_track (int track_type, int track_nbr, const PLAY_SOURCE_TRACK_CONF_BLOCK *new_track_conf_ptr, int flags)
{
	int		nbr_src;
	int		insert_pos;
	int	ret_val;
	TrackInfoSrc::Source	*track_conf_ptr;

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (track_type, track_nbr, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("add_source_track: Error: bad destination track type.\n");
		return (-1);
	}

	/* S'il s'agit d'une piste d'Audio In, regarde si elle est valide */
	if (! check_track_validity (new_track_conf_ptr->track_type, new_track_conf_ptr->track_nbr))
	{
		return (3);
	}

	/* Cherche si la piste est deja dans les sources; cherche aussi ou inserer
	   la nouvelle piste. */
	ret_val = check_track_is_in_sources (track_type, track_nbr, &insert_pos, new_track_conf_ptr);

	/* La piste existe deja. On va donc seulement modifier ses parametres */
	if (ret_val == 1)
	{
		if ((flags & MIX_PRESET_MASK) != MIX_PRESET_REPL)
		{
			if ((flags & MIX_PRESET_NO_VOL) == 0)
			{
				track_conf_ptr [insert_pos].conf.inpvol = new_track_conf_ptr->inpvol;
			}
			if ((flags & MIX_PRESET_NO_PAN) == 0)
			{
				track_conf_ptr [insert_pos].conf.inppan = new_track_conf_ptr->inppan;
			}
		}
		return (1);
	}

	else if (ret_val < 0)
	{
		LOG_printf ("add_source_track: Error: couldn't get source track information.\n");
		return (-1);
	}

	/* Regarde si en connectant cette piste on provoque un bouclage */
	if (   new_track_conf_ptr->track_type == Pattern_TYPE_FX
	    && track_type == Pattern_TYPE_FX)
	{
		if (find_track_loop (new_track_conf_ptr->track_nbr, track_nbr))
		{
			return (2);
		}
	}

	/* Insere la piste dans la liste */
	if (insert_pos < nbr_src)
	{
		for (int index = nbr_src; index > insert_pos; --index)
		{
			track_conf_ptr [index] = track_conf_ptr [index - 1];
		}
	}
	copy_track_conf_without_state (track_conf_ptr [insert_pos].conf, *new_track_conf_ptr);
	track_conf_ptr [insert_pos].conf.cr.old_vol [0] = 0;
	track_conf_ptr [insert_pos].conf.cr.old_vol [1] = 0;
	track_conf_ptr [insert_pos].conf.cr._gain [0].set_initial_flag (true);
	track_conf_ptr [insert_pos].conf.cr._gain [1].set_initial_flag (true);
	track_conf_ptr [insert_pos].meter.set_sample_freq (MIX_replay_freq);
	track_conf_ptr [insert_pos].meter.clear_buffers ();
	nbr_src ++;
	if (set_nbr_source_tracks (track_type, track_nbr, nbr_src))
	{
		LOG_printf ("add_source_track: Error: bad destination track type.");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: change_source_track                                            */
/*      Description: Change une piste source a une piste d'effet ou d'Audio */
/*                   Out. Aucun test d'integrite n'est effectue pour savoir */
/*                   si la nouvelle piste ne va pas introduire de bouclage, */
/*                   ou si elle est deja presente.                          */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste sur laquelle on va modifier une    */
/*                      source.                                             */
/*        - track_nbr: Numero de la piste.                                  */
/*        - position: numero de la source                                   */
/*        - new_track_conf_ptr: pointeur sur la structure de configuration  */
/*                              de la piste.                                */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 si la piste etait deja dans les sources (rien n'est       */
/*                change),                                                  */
/*              2 si l'introduction de la piste aurait induit un bouclage   */
/*                (operation annulee).                                      */
/*              -1 si erreur.                                               */
/*==========================================================================*/

int	Player::change_source_track (int track_type, int track_nbr, int position, const PLAY_SOURCE_TRACK_CONF_BLOCK *new_track_conf_ptr)
{
	int		nbr_src;
	TrackInfoSrc::Source	*track_conf_ptr;

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (track_type, track_nbr, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("change_source_track: Error: bad destination track type.\n");
		return (-1);
	}

	copy_track_conf_without_state (track_conf_ptr [position].conf, *new_track_conf_ptr);
	track_conf_ptr [position].conf.cr.old_vol [0] = 0;
	track_conf_ptr [position].conf.cr.old_vol [1] = 0;

	return (0);
}



/*==========================================================================*/
/*      Nom: remove_source_track                                            */
/*      Description: Retire une piste des sources d'une piste d'effet ou    */
/*                   d'Audio Out, si cela est possible.                     */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste sur laquelle on va ajouter une     */
/*                      source.                                             */
/*        - track_nbr: Numero de la piste.                                  */
/*        - new_track_conf_ptr: pointeur sur la structure de configuration  */
/*                              de la piste a retirer. Seuls les champs     */
/*                              track_type, track_nbr et wet_flag ont       */
/*                              besoin d'etre rempli.                       */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 si la piste n'etait pas dans les sources.                 */
/*              2 si la piste etait la derniere et qu'on ne pouvait pas     */
/*                l'enlever.                                                */
/*              -1 si erreur.                                               */
/*==========================================================================*/

int	Player::remove_source_track (int track_type, int track_nbr, const PLAY_SOURCE_TRACK_CONF_BLOCK *new_track_conf_ptr)
{
	int		src_cnt;
	int		nbr_src;
	TrackInfoSrc::Source	*track_conf_ptr;

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (track_type, track_nbr, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("remove_source_track: Error: bad destination track type.");
		return (-1);
	}

	for (src_cnt = 0; src_cnt < nbr_src; src_cnt ++)
	{
		if (   track_conf_ptr [src_cnt].conf.track_type == new_track_conf_ptr->track_type
		    && track_conf_ptr [src_cnt].conf.track_nbr == new_track_conf_ptr->track_nbr)
		{
			if (   new_track_conf_ptr->track_type != Pattern_TYPE_FX
			    || new_track_conf_ptr->wet_flag == track_conf_ptr [src_cnt].conf.wet_flag)
			{
				/* On ne peut pas retirer la derniere piste source */
				if (nbr_src <= 1)
				{
					return (2);
				}

				if (src_cnt < nbr_src - 1)
				{
					for (int index = src_cnt; index < nbr_src - 1; ++index)
					{
						track_conf_ptr [index] = track_conf_ptr [index + 1];
					}
				}
				nbr_src --;
				if (set_nbr_source_tracks (track_type, track_nbr, nbr_src))
				{
					LOG_printf ("remove_source_track: Error: bad destination track type.");
					return (-1);
				}

				return (0);
			}
		}
	}

	return (1);
}



/*==========================================================================*/
/*      Nom: check_track_validity                                           */
/*      Description: Teste si une piste donnee existe bien reellement.      */
/*                   Permet particulierement de tester les pistes AIN/AOU   */
/*                   qui feraient en fait partie d'une piste stereo situee  */
/*                   juste avant (elle n'existe donc pas a proprement       */
/*                   parler).                                               */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste concernee.                         */
/*        - track_nbr: Numero de la piste.                                  */
/*      Retour: true si la piste existe, false sinon.                       */
/*==========================================================================*/

bool	Player::check_track_validity (int track_type, int track_nbr) const
{
	int		nbr_tracks;
	int		track_count;
	int		stereo;

	if (   track_type != GTK_TRACK_TYPE_AOU
	    && track_type != Pattern_TYPE_AIN)
	{
		if (track_nbr < GTK_nbr_tracks [track_type])
		{
			return (true);
		}
		else
		{
			return (false);
		}
	}
	
	nbr_tracks = MIN (GTK_nbr_tracks [track_type], track_nbr + 1);
	for (track_count = 0; track_count < nbr_tracks; track_count ++)
	{
		if (track_nbr == track_count)
		{
			return (true);
		}

		stereo = get_track_stereo (track_type, track_count, false) - 1;
		while (stereo > 0)
		{
			track_count ++;
			stereo --;
		}
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: check_track_is_in_sources                                      */
/*      Description: Regarde si une piste donnee fait partie des sources    */
/*                   d'une certaine piste.                                  */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste concernee.                         */
/*        - track_nbr: Numero de la piste.                                  */
/*        - new_track_conf_ptr: pointeur sur la structure de configuration  */
/*                              de la piste a examiner. Seuls les champs    */
/*                              track_type, track_nbr et wet_flag ont       */
/*                              besoin d'etre rempli.                       */
/*      Parametres en sortie:                                               */
/*        - position_ptr: pointeur sur la position de la piste dans les     */
/*                        sources si on l'a trouvee, ou indique l'endroit   */
/*                        ou cette piste devrait etre inseree.              */
/*                        Si le pointeur est NULL, ne fait rien.            */
/*      Retour: 0 si la piste n'est pas trouvee,                            */
/*              1 si la piste est trouvee,                                  */
/*              -1 si erreur.                                               */
/*==========================================================================*/

int	Player::check_track_is_in_sources (int track_type, int track_nbr, int *position_ptr, const PLAY_SOURCE_TRACK_CONF_BLOCK *new_track_conf_ptr)
{
	int		src_cnt;
	int		nbr_src;
	TrackInfoSrc::Source	*track_conf_ptr;

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (track_type, track_nbr, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("check_track_is_in_sources: Error: bad destination track type.\n");
		return (-1);
	}

	for (src_cnt = 0; src_cnt < nbr_src; src_cnt ++)
	{
		if (   track_conf_ptr [src_cnt].conf.track_type == new_track_conf_ptr->track_type
		    && track_conf_ptr [src_cnt].conf.track_nbr == new_track_conf_ptr->track_nbr)
		{
			if (   new_track_conf_ptr->track_type != Pattern_TYPE_FX
			    || track_conf_ptr [src_cnt].conf.wet_flag == new_track_conf_ptr->wet_flag)
			{
				if (position_ptr != NULL)
				{
					*position_ptr = src_cnt;
				}
				return (1);
			}
		}
		else if (       track_conf_ptr [src_cnt].conf.track_type >  new_track_conf_ptr->track_type
		         || (   track_conf_ptr [src_cnt].conf.track_type == new_track_conf_ptr->track_type
		             && track_conf_ptr [src_cnt].conf.track_nbr  >= new_track_conf_ptr->track_nbr))
		{
			break;
		}
	}

	if (position_ptr != NULL)
	{
		*position_ptr = src_cnt;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: find_track_to_add                                              */
/*      Description:                                                        */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste concernee.                         */
/*        - track_nbr: Numero de la piste.                                  */
/*      Parametres en entree/sortie:                                        */
/*        - new_track_conf_ptr: pointeur sur la structure de configuration  */
/*                              de la piste dont on veut tester la          */
/*                              possibilite d'insertion. Seuls les champs   */
/*                              track_type, track_nbr et wet_flag ont       */
/*                              besoin d'etre rempli. Ces champs sont       */
/*                              modifies si besoin est.                     */
/*      Retour: 0 si la piste est inserable telle-quelle,                   */
/*              1 si on a du changer le numero de la piste,                 */
/*                les donnees de la piste d'entree ont alors ete modifiees. */
/*              2 si il n'est pas possible d'inserer une nouvelle piste,    */
/*                les donnees de la piste d'entree ont alors ete modifiees. */
/*              -1 si erreur.                                               */
/*==========================================================================*/

int	Player::find_track_to_add (int track_type, int track_nbr, PLAY_SOURCE_TRACK_CONF_BLOCK *new_track_conf_ptr)
{
	int	ret_val;
	int	track_cnt;

	new_track_conf_ptr->wet_flag =    new_track_conf_ptr->wet_flag
	                               || new_track_conf_ptr->track_type != Pattern_TYPE_FX;

	/* Cherche si c'est possible d'inserer la piste */
	for (track_cnt = -1; track_cnt < GTK_nbr_tracks [new_track_conf_ptr->track_type]; track_cnt ++)
	{
		if (track_cnt >= 0)
		{
			new_track_conf_ptr->track_nbr = track_cnt;
		}

		if (check_track_validity (new_track_conf_ptr->track_type, new_track_conf_ptr->track_nbr))
		{
			ret_val = check_track_is_in_sources (track_type, track_nbr, NULL, new_track_conf_ptr);
			if (ret_val == 0)
			{
				if (   new_track_conf_ptr->track_type == Pattern_TYPE_FX
				    && track_type == Pattern_TYPE_FX)
				{
					if (! find_track_loop (new_track_conf_ptr->track_nbr, track_nbr))
					{
						return ((track_cnt < 0) ? 0 : 1);
					}
				}
				else
				{
					return ((track_cnt < 0) ? 0 : 1);
				}
			}

			else if (ret_val < 0)
			{
				LOG_printf ("find_track_to_add: Error: bad destination track type.\n");
				return (-1);
			}
		}
	}

	return (2);
}



/*==========================================================================*/
/*      Nom: find_track_loop                                                */
/*      Description: Recherche si une piste d'effet est dans l'arborescence */
/*                   d'une autre piste d'effet.                             */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste d'effet a scanner                     */
/*        - track_to_find: piste d'effet a rechercher.                      */
/*      Retour: true si la piste est trouvee dans l'arborescence, et donc   */
/*              qu'il va peut-etre y avoir une boucle si insertion de la    */
/*              premiere dans les sources de la deuxieme, false sinon.      */
/*==========================================================================*/

bool	Player::find_track_loop (int track, int track_to_find)
{
	int		nbr_src;
	int		src_cnt;
	int		track_to_scan;
	TrackInfoSrc::Source	*track_conf_ptr;

	if (track == track_to_find)
	{
		return (true);
	}

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (Pattern_TYPE_FX, track, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("find_track_loop: Error: bad destination track type.\n");
		return (true);
	}

	for (src_cnt = 0; src_cnt < nbr_src; src_cnt ++)
	{
		if (track_conf_ptr [src_cnt].conf.track_type == Pattern_TYPE_FX)
		{
			track_to_scan = track_conf_ptr [src_cnt].conf.track_nbr;

			/* Le premier test n'est pas indispensable
			   mais accelere un peu les choses */
			if (track_to_scan == track_to_find)
			{
				return (true);
			}

			else if (find_track_loop (track_to_scan, track_to_find))
			{
				return (true);
			}
		}
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: get_track_nbr_s_tracks                                         */
/*      Description: Renvoie des informations sur les sources d'une piste:  */
/*                   le nombre de sources et un pointeur sur le tableau des */
/*                   configurations de ces sources.                         */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*      Parametres en sortie:                                               */
/*        - nbr_s_tracks_ptr: pointeur sur le nombre de pistes sources.     */
/*        - track_conf_ptr_ptr: pointeur sur le pointeur des configurations */
/*                              des pistes sources.                         */
/*      Retour: 0 si tout s'est bien passe, ou -1 si le type de la piste ne */
/*              correspondait pas a un type utilisant des pistes sources.   */
/*==========================================================================*/

int	Player::get_track_nbr_s_tracks (int track_type, int track_nbr, int *nbr_s_tracks_ptr, TrackInfoSrc::Source **track_conf_ptr_ptr)
{
	Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];
	if (   track_type != GTK_TRACK_TYPE_AOU
	    && track_type != Pattern_TYPE_FX)
	{
		return (-1);
	}

	*nbr_s_tracks_ptr = track_info.mix.src.nbr_s_tracks;
	*track_conf_ptr_ptr = track_info.mix.src.track_conf;

	return (0);
}



/*==========================================================================*/
/*      Nom: load_mix_preset                                                */
/*      Description: Applique un preset de mixage sur une piste.            */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*        - track_type: type de la piste sur laquelle on applique le preset */
/*                      (FX ou Audio Out).                                  */
/*        - track_nbr: numero de la piste.                                  */
/*        - preset_nbr: numero du preset a appliquer.                       */
/*        - flags: permet de savoir de quelle facon  ajouter la piste       */
/*                 source (type MIX_PRESET_???).                            */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

int	Player::load_mix_preset (int track_type, int track_nbr, int preset_nbr, int flags)
{
	int		nbr_src;
	int		src_cnt;
	PLAY_SOURCE_TRACK_CONF_BLOCK	track_conf;
	TrackInfoSrc::Source	*track_conf_ptr;

	/* Si on n'a pas precise la destination, on recherche la piste par defaut
	   sur laquelle on peut appliquer le preset. */
	if (   track_type < 0
	    || track_nbr < 0)
	{
		track_type = MIXP_get_track_type (preset_nbr);
		if (   track_type != Pattern_TYPE_FX
		    && track_type != GTK_TRACK_TYPE_AOU)
		{
			LOG_printf ("load_mix_preset: Error: bad destination track type.\n");
			return (-1);
		}
		track_nbr = MIXP_get_track_number (preset_nbr);
		if (   track_nbr < 0
		    || track_nbr >= GTK_nbr_tracks [track_type])
		{
			LOG_printf ("load_mix_preset: Error: bad destination track number.\n");
			return (-1);
		}
	}

	TrackInfo &		track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (track_type, track_nbr, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("load_mix_preset: Error: bad destination track type (b).\n");
		return (-1);
	}

	nbr_src = MIXP_get_nbr_source_tracks (preset_nbr);

	if ((flags & MIX_PRESET_MASK) == MIX_PRESET_REPL)
	{
		/* Fixe le nombre de sources a 1 (le mininmum) */
		if (set_nbr_source_tracks (track_type, track_nbr, 1))
		{
			LOG_printf ("load_mix_preset: Error: bad destination track type.\n");
			return (-1);
		}
	}

	/* Ajoute chaque configuration de source */
	for (src_cnt = 0; src_cnt < nbr_src; src_cnt ++)
	{
		MIXP_get_source_track (preset_nbr, src_cnt, &track_conf);
		if (   src_cnt > 0
		    || (flags & MIX_PRESET_MASK) == MIX_PRESET_ADD)
		{
			add_source_track (track_type, track_nbr, &track_conf, flags);
		}
		else
		{
			copy_track_conf_without_state (track_conf_ptr [0].conf, track_conf);
		}
	}
	if (track_type == GTK_TRACK_TYPE_AOU)
	{
		update_aou_source_tracks_with_aou_source_conf (track_nbr);
	}
	
	/* Initialise les parametres propres a la piste */
	if ((flags & Player::MIX_PRESET_NO_DEST) == 0)
	{
		if ((flags & MIX_PRESET_NO_VOL) == 0)
		{
			set_lin_volume (track_info, MIXP_get_volume (preset_nbr, false));
			set_dry_lin_volume (track_info, MIXP_get_volume (preset_nbr, true));
		}
		if ((flags & MIX_PRESET_NO_PAN) == 0)
		{
			track_info.score.track.panning = (WORD) (MIXP_get_panning (preset_nbr, false) << 4);
			track_info.score.track.drypan = (WORD) (MIXP_get_panning (preset_nbr, true) << 4);
		}
		set_track_stereo (track_type, track_nbr, MIXP_get_stereo (preset_nbr));
	}

	return (0);
}



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

int	Player::save_mix_preset (int track_type, int track_nbr, int preset_nbr)
{
	int		nbr_src;
	int		src_cnt;
	TrackInfoSrc::Source	*track_conf_ptr;
	const Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (track_type, track_nbr, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("save_mix_preset: Error: bad destination track type.\n");
		return (-1);
	}

	/* Fixe le nombre de sources */
	if (MIXP_set_nbr_source_tracks (preset_nbr, nbr_src))
	{
		LOG_printf ("save_mix_preset: Error: couldn't change preset size.\n");
		return (-1);
	}

	/* Recopie chaque configuration de source */
	if (track_type == GTK_TRACK_TYPE_AOU)
	{
		update_aou_source_conf_with_aou_source_tracks (track_nbr);
	}
	for (src_cnt = 0; src_cnt < nbr_src; src_cnt ++)
	{
		MIXP_set_source_track (preset_nbr, src_cnt, &track_conf_ptr [src_cnt].conf);
	}
	
	MIXP_set_track_type (preset_nbr, track_type);
	MIXP_set_track_number (preset_nbr, track_nbr);
	MIXP_set_volume (preset_nbr, false, track_info.score.track.volume_lin);
	MIXP_set_volume (preset_nbr, true, track_info.score.track.dryvol_lin);
	MIXP_set_panning (preset_nbr, false, track_info.score.track.panning >> 4);
	MIXP_set_panning (preset_nbr, true, track_info.score.track.drypan >> 4);
	MIXP_set_stereo (preset_nbr, track_info.stereo);

	return (0);
}



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/

int	Player::update_aou_source_tracks_with_aou_source_conf (int track_nbr)
{
	int		track;
	int		type;
	bool		dry_flag;
	int		src_cnt;
	int		nbr_src;
	TrackInfoSrc::Source	*track_conf_ptr;

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (GTK_TRACK_TYPE_AOU, track_nbr, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("update_aou_source_tracks_with_aou_source_conf: Error: bad destination track type.\n");
		return (-1);
	}

	for (src_cnt = 0; src_cnt < nbr_src; src_cnt ++)
	{
		type     =   track_conf_ptr [src_cnt].conf.track_type;
		track    =   track_conf_ptr [src_cnt].conf.track_nbr;
		dry_flag = ! track_conf_ptr [src_cnt].conf.wet_flag;

		Player::set_track_lin_volume (
			type, track, dry_flag, track_conf_ptr [src_cnt].conf.inpvol
		);
		Player::set_track_panning (
			type, track, dry_flag, track_conf_ptr [src_cnt].conf.inppan
		);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/

int	Player::update_aou_source_conf_with_aou_source_tracks (int track_nbr)
{
	int		track;
	int		type;
	bool		dry_flag;
	int		src_cnt;
	int		nbr_src;
	TrackInfoSrc::Source	*track_conf_ptr;

	/* Recueille les informations sur les sources */
	if (get_track_nbr_s_tracks (GTK_TRACK_TYPE_AOU, track_nbr, &nbr_src, &track_conf_ptr))
	{
		LOG_printf ("update_aou_source_conf_with_aou_source_tracks: Error: bad destination track type.\n");
		return (-1);
	}

	for (src_cnt = 0; src_cnt < nbr_src; src_cnt ++)
	{
		type     =   track_conf_ptr [src_cnt].conf.track_type;
		track    =   track_conf_ptr [src_cnt].conf.track_nbr;
		dry_flag = ! track_conf_ptr [src_cnt].conf.wet_flag;

		track_conf_ptr [src_cnt].conf.inpvol = get_track_lin_volume (type, track, dry_flag);
		track_conf_ptr [src_cnt].conf.inppan = get_track_panning (type, track, dry_flag);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: get_track_name                                                 */
/*      Description: Renvoie le nom d'une piste.                            */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*      Parametres en sortie:                                               */
/*        - name_0: pointeur sur la chaine contenant le nom de la piste,    */
/*                  termine par 0. L'espace doit etre deja reserve, et      */
/*                  suffisant pour accueillir la chaine.                    */
/*==========================================================================*/

void	Player::get_track_name (int track_type, int track_nbr, char *name_0) const
{
	const Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];
	BASE_copy_string (track_info.name, name_0,
							TRK_NAME_LEN, TRK_NAME_LEN);
	name_0 [TRK_NAME_LEN] = 0;
}



/*==========================================================================*/
/*      Nom: set_track_name                                                 */
/*      Description: Nomme une piste.                                       */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - name_0: pointeur sur la chaine contenant le nom de la piste,    */
/*                  termine par 0.                                          */
/*==========================================================================*/

void	Player::set_track_name (int track_type, int track_nbr, const char *name_0)
{
	Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];
	BASE_copy_string (name_0, track_info.name,
	                  strlen (name_0), TRK_NAME_LEN);
}



/*==========================================================================*/
/*      Nom: get_track_onoff                                                */
/*      Description: Renvoie l'etat d'activite d'une piste.                 */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste demandee.                          */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie dry.           */
/*      Retour: true si la piste est active.                                */
/*==========================================================================*/

bool	Player::get_track_onoff (int track_type, int track_nbr, bool dry_flag) const
{
	if (dry_flag)
	{
		return (! _track_info [_track_info_list [track_type] [track_nbr]].dry_mute_flag);
	}
	
	return (! _track_info [_track_info_list [track_type] [track_nbr]].wet_mute_flag);
}



/*==========================================================================*/
/*      Nom: set_track_onoff                                                */
/*      Description: Fixe l'etat d'activite d'une piste.                    */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie dry.           */
/*        - state: true si la piste doit etre active, false si elle doit    */
/*                 etre coupee.                                             */
/*==========================================================================*/

void	Player::set_track_onoff (int track_type, int track_nbr, bool dry_flag, bool state)
{
	if (dry_flag)
	{
		_track_info [_track_info_list [track_type] [track_nbr]].dry_mute_flag = ! state;
	}
	else
	{
		_track_info [_track_info_list [track_type] [track_nbr]].wet_mute_flag = ! state;
	}
}



/*==========================================================================*/
/*      Nom: get_track_panning                                              */
/*      Description: Renvoie la balance d'une piste.                        */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie dry.           */
/*      Retour: la balance.                                                 */
/*==========================================================================*/

LWORD	Player::get_track_panning (int track_type, int track_nbr, bool dry_flag) const
{
	const Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];

	if (dry_flag)
	{
		return (track_info.score.track.drypan);
	}

	return (track_info.score.track.panning);
}



/*==========================================================================*/
/*      Nom: set_track_panning                                              */
/*      Description: Fixe la balance d'une piste.                           */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie wet.           */
/*        - pan: Balance (0000...FFFF).                                     */
/*==========================================================================*/

void	Player::set_track_panning (int track_type, int track_nbr, bool dry_flag, LWORD pan)
{
	Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];
	Player::set_panning (track_info, dry_flag, pan);
}



/*==========================================================================*/
/*      Nom: get_track_lin_volume                                           */
/*      Description: Renvoie le volume lineaire d'une piste specifiee.      */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie wet.           */
/*      Retour: Le volume (0...1000...FFFF)                                 */
/*==========================================================================*/

LWORD	Player::get_track_lin_volume (int track_type, int track_nbr, bool dry_flag) const
{
	const Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];

	if (dry_flag)
	{
		return (track_info.score.track.dryvol_lin);
	}

	return (track_info.score.track.volume_lin);
}



/*==========================================================================*/
/*      Nom: set_track_lin_volume                                           */
/*      Description: Fixe le volume lineaire d'une piste specifiee.         */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie dry.           */
/*        - volume: le volume (0...1000...FFFF)                             */
/*==========================================================================*/

void	Player::set_track_lin_volume (int track_type, int track_nbr, bool dry_flag, LWORD volume)
{
	Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];
	if (dry_flag)
	{
		Player::set_dry_lin_volume (track_info, volume);
	}
	else
	{
		Player::set_lin_volume (track_info, volume);
	}
}



/*==========================================================================*/
/*      Nom: get_track_log_volume                                           */
/*      Description: Renvoie le volume logarithmique d'une piste specifiee  */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie wet.           */
/*      Retour: Le volume (0...C00...FFF)                                   */
/*==========================================================================*/

LWORD	Player::get_track_log_volume (int track_type, int track_nbr, bool dry_flag) const
{
	const Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];

	if (dry_flag)
	{
		return (track_info.score.track.dryvol_log);
	}

	return (track_info.score.track.volume_log);
}



/*==========================================================================*/
/*      Nom: set_track_log_volume                                           */
/*      Description: Fixe le volume logarithmique d'une piste specifiee     */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie dry.           */
/*        - volume: le volume (0...C00...FFF)                               */
/*==========================================================================*/

void	Player::set_track_log_volume (int track_type, int track_nbr, bool dry_flag, LWORD volume)
{
	Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];
	if (dry_flag)
	{
		Player::set_dry_log_volume (track_info, volume);
	}
	else
	{
		Player::set_log_volume (track_info, volume);
	}
}



/*==========================================================================*/
/*      Nom: lin_to_log_volume                                              */
/*      Description: Convertit un volume de piste lineaire en volume log.   */
/*      Parametres en entree:                                               */
/*        - lin_vol: volume lineaire (0...1000...FFFF)                      */
/*      Retour: volume logarithmique correspondant (0...C00...FFF).         */
/*==========================================================================*/

LWORD	Player::lin_to_log_volume (LWORD lin_vol)
{
	LWORD		log_vol;

	if (lin_vol == 0)
	{
		log_vol = 0;
	}

	else
	{
		/* Recherche de la partie entiere de la puissance de 2 */
		log_vol = 0x1000;
		do
		{
			log_vol -= 0x100;
			lin_vol <<= 1;
		}
		while ((lin_vol & 0x10000) == 0);
		lin_vol = (lin_vol >> 8) & 0xFF;

		/* Recherche de la partie fractionnaire */
		log_vol += (int) _track_vol_table_log [lin_vol];
	}

	return (log_vol);
}



/*==========================================================================*/
/*      Nom: log_to_lin_volume                                              */
/*      Description: Convertit un volume de piste log en volume lineaire.   */
/*      Parametres en entree:                                               */
/*        - lin_vol: volume logarithmique (0...C00...FFF)                   */
/*      Retour: volume lineaire correspondant (0...1000...FFFF).            */
/*==========================================================================*/

LWORD	Player::log_to_lin_volume (LWORD log_vol)
{
	LWORD		lin_vol;

	if (log_vol == 0)
	{
		lin_vol = 0;
	}

	else
	{
		lin_vol =    _track_vol_table_exp [log_vol & 0xFF]
					 >> (0xF - (log_vol >> 8));
	}

	return (lin_vol);
}



/*==========================================================================*/
/*      Nom: get_track_stereo                                               */
/*      Description: Renvoie la stereo d'une piste donnee.                  */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - dry_flag: indique qu'on veut manipuler la partie dry.           */
/*      Retour: Nombre de voies de la piste.                                */
/*==========================================================================*/

int	Player::get_track_stereo (int track_type, int track_nbr, bool dry_flag) const
{
	const Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];

	if (dry_flag && track_type == Pattern_TYPE_FX)
	{
		return (2);	/* Les pistes FX ont toujours leur dry en stereo. */
	}

	return (track_info.stereo);
}



/*==========================================================================*/
/*      Nom: set_track_stereo                                               */
/*      Description: Fixe la stereo d'une piste si c'est possible. Pour les */
/*                   pistes de FX, c'est toujours le wet.                   */
/*                   GTK_mutex must be locked.                              */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  */
/*        - stereo: nombre de voies demandees.                              */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 si on n'a pas pu changer la stereo,                       */
/*              -1 si erreur.                                               */
/*==========================================================================*/

int	Player::set_track_stereo (int track_type, int track_nbr, int stereo)
{
	int	ret_val;

	if (stereo < 1)
	{
		return (1);
	}

	TrackInfo &		track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];

	ret_val = 0;
	switch (track_type)
	{
	case	Pattern_TYPE_SPL:		// Ne peut etre change
	case	Pattern_TYPE_AIN:		// A voir
	case	Pattern_TYPE_MID:		// Ne peut etre change
	case	GTK_TRACK_TYPE_AOU:	// A voir
		ret_val = 1;
		break;

	case	Pattern_TYPE_FX:
		ret_val = fx_fx_set_stereo (track_info, stereo);
		if (ret_val < 0)
		{
			LOG_printf ("set_track_stereo: Error: failed to change stereo of FX track # %d.\n", track_nbr);
			ret_val = -1;
		}
		else
		{
			ret_val = (ret_val != stereo) ? 1 : 0;
		}
		break;
	}

	return (ret_val);
}



/*==========================================================================*/
/*      Nom: get_tempo                                                      */
/*      Description: Renvoie le tempo du module.                            */
/*      Retour: Le tempo, en BPM.                                           */
/*==========================================================================*/

double	Player::get_tempo () const
{
	return (_score_info.tempo);
}



/*==========================================================================*/
/*      Nom: set_tempo                                                      */
/*      Description: Fixe le tempo du module.                               */
/*      Parametres en entree:                                               */
/*        - tempo: tempo en BPM (32 a 999).                                 */
/*==========================================================================*/

void	Player::set_tempo (double tempo)
{
	std::lock_guard <std::mutex>	lock (GTK_mutex);

	tempo = MIN (tempo, Player::MAX_TEMPO);
	tempo = MAX (tempo, Player::MIN_TEMPO);
	_score_info.tempo = tempo;
	SimpleCalculator::set_spec_var (SimpleCalculator::TEMPO, tempo);
}



/*==========================================================================*/
/*      Nom: get_speed                                                      */
/*      Description: Renvoie la vitesse courante du module.                 */
/*      Retour: Vitesse                                                     */
/*==========================================================================*/

int	Player::get_speed () const
{
	return (_score_info.speed);
}



/*==========================================================================*/
/*      Nom: set_speed                                                      */
/*      Description: Fixe la vitesse courante du module                     */
/*      Parametres en entree:                                               */
/*        - speed: vitesse (1 - 255).                                       */
/*==========================================================================*/

void	Player::set_speed (int speed)
{
	std::lock_guard <std::mutex>	lock (GTK_mutex);

	speed = MIN (speed, Player::MAX_SPEED);
	speed = MAX (speed, Player::MIN_SPEED);
	_score_info.speed = speed;
}



/*==========================================================================*/
/*      Nom: get_lin_master_volume                                          */
/*      Description: Renvoie le master volume lineaire.                     */
/*      Retour: volume lineaire (0...FFF).                                  */
/*==========================================================================*/

LWORD	Player::get_lin_master_volume ()
{
	LWORD		vol;
	const Player::TrackInfo &	aou_track_info =
		_track_info [_track_info_list [GTK_TRACK_TYPE_AOU] [0]];
	vol = (LWORD)aou_track_info.score.track.volume_lin;
	vol = MIN (vol, 0x0FFFF);
	vol = MAX (vol, 0);

	return (vol);
}



/*==========================================================================*/
/*      Nom: set_lin_master_volume                                          */
/*      Description: Fixe le master volume lineaire. En fait il s'agit      */
/*                   de fixer les volumes de toutes les voies Audio Out au  */
/*                   meme niveau.                                           */
/*      Parametres en entree:                                               */
/*        - vol: Le nouveau master volume.                                  */
/*==========================================================================*/

void	Player::set_lin_master_volume (LWORD vol)
{
	vol = MIN (vol, 0xFFFF);
	vol = MAX (vol, 0);

	for (int track = 0; track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; track ++)
	{
		Player::TrackInfo &	aou_track_info =
			_track_info [_track_info_list [GTK_TRACK_TYPE_AOU] [track]];
		Player::set_lin_volume (aou_track_info, vol);
	}
}



/*==========================================================================*/
/*      Nom: get_log_master_volume                                          */
/*      Description: Renvoie le master volume logarithmique.                */
/*      Retour: volume logarithmique (0...800).                             */
/*==========================================================================*/

LWORD	Player::get_log_master_volume ()
{
	LWORD		vol;
	const Player::TrackInfo &	aou_track_info =
		_track_info [_track_info_list [GTK_TRACK_TYPE_AOU] [0]];
	vol = (LWORD)aou_track_info.score.track.volume_log + 0x0400;
	vol = MIN (vol, 0x13FF);
	vol = MAX (vol, 0);

	return (vol);
}



/*==========================================================================*/
/*      Nom: set_log_master_volume                                          */
/*      Description: Fixe le master volume logarithmique. En fait il s'agit */
/*                   de fixer les volumes de toutes les voies Audio Out au  */
/*                   meme niveau.                                           */
/*      Parametres en entree:                                               */
/*        - vol: Le nouveau master volume.                                  */
/*==========================================================================*/

void	Player::set_log_master_volume (LWORD vol)
{
	vol -= 0x0400;
	vol = MIN (vol, 0x1000);
	vol = MAX (vol, 0);

	for (int track = 0; track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; track ++)
	{
		Player::TrackInfo &	aou_track_info =
			_track_info [_track_info_list [GTK_TRACK_TYPE_AOU] [track]];
		Player::set_log_volume (aou_track_info, vol);
	}
}



/*==========================================================================*/
/*      Nom: get_interpolation_flag                                         */
/*      Description: Renvoie l'etat d'interpolation d'une piste de samples. */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste.                                      */
/*      Retour: true s'il y a interpolation.                                */
/*==========================================================================*/

bool	Player::get_interpolation_flag (int track)
{
	const TrackInfo &	track_info =
		_track_info [_track_info_list [Pattern_TYPE_SPL] [track]];
	const int		voice_index = track_info.mix.spl.voice_index_arr [0];
	const Voice &	voice = _voice_arr [voice_index];

	return (voice.spl.interpol_flag);
}



/*==========================================================================*/
/*      Nom: set_interpolation_flag                                         */
/*      Description: Fixe l'etat d'interpolation d'un piste de samples.     */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste.                                      */
/*        - interpol_flag: true si on doit interpoler les samples.          */
/*==========================================================================*/

void	Player::set_interpolation_flag (int track, bool interpol_flag)
{
	Player::TrackInfo	&	track_info =
		_track_info [_track_info_list [Pattern_TYPE_SPL] [track]];

	set_interpolation_flag (track_info, interpol_flag);
}



void	Player::set_interpolation_flag (TrackInfo &track_info, bool interpol_flag)
{
	assert (&track_info != 0);

	for (int voice_cnt = 0; voice_cnt < track_info.mix.spl.nbr_voices; ++voice_cnt)
	{
		const int		voice_index = track_info.mix.spl.voice_index_arr [voice_cnt];
		Voice &			voice = _voice_arr [voice_index];
		voice.spl.interpol_flag = interpol_flag;
	}
}



/*==========================================================================*/
/*      Nom: no_mute                                                        */
/*      Description: Active toutes les voies.                               */
/*==========================================================================*/

void	Player::no_mute ()
{
	int		track_type;
	int		track;

	for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
	{
		for (track = 0; track < GTK_nbr_tracks [track_type]; track ++)
		{
			set_track_onoff (track_type, track, false, true);
			set_track_onoff (track_type, track, true, true);
		}
	}
}



/*==========================================================================*/
/*      Nom: invert_all_mute                                                */
/*      Description: Inverse l'etat muet/actif de toutes les voies.         */
/*==========================================================================*/

void	Player::invert_all_mute ()
{
	int		track_type;
	int		track;
	bool		active_flag;

	for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
	{
		for (track = 0; track < GTK_nbr_tracks [track_type]; track ++)
		{
			active_flag = ! get_track_onoff (track_type, track, false);
			set_track_onoff (track_type, track, false, active_flag);
			set_track_onoff (track_type, track, true, active_flag);
		}
	}
}



/*==========================================================================*/
/*      Nom: default_interpolation                                          */
/*      Description: Remet par defaut l'interpolation de toutes les voies.  */
/*==========================================================================*/

void	Player::default_interpolation ()
{
	int		track;

	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; track ++)
	{
		set_interpolation_flag (track, _default_interpolation_flag);
	}
}



/*==========================================================================*/
/*      Nom: get_clipping_level                                             */
/*      Description: Renvoie le niveau de clipping d'une piste AudioOut.    */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste de sortie.                            */
/*      Retour: Niveau de clipping. Si la piste a clippe, la valeur sort du */
/*              domaine representable par 24 bits signes.                   */
/*==========================================================================*/

float	Player::get_clipping_level (int track) const
{
	return (_clipping_level [track]);
}



/*==========================================================================*/
/*      Nom: set_clipping_level                                             */
/*      Description: Fixe le niveau de clipping (il faut d'abord l'avoir    */
/*                   mesure).                                               */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste de sortie.                            */
/*        - level: niveau de clipping de la piste (positif). Si la valeur   */
/*                 n'est pas representable sur 24 bits signes, la piste a   */
/*                 clippe.                                                  */
/*==========================================================================*/

void	Player::set_clipping_level (int track, float level)
{
	_clipping_level [track] = level;
}




/*==========================================================================*/
/*      Nom: get_max_clipping_level                                         */
/*      Description: Renvoie le niveau de clipping maximum atteint depuis   */
/*                   la derniere reinitialisation du niveau.                */
/*      Retour: Niveau de clipping. Si la piste a clippe, la valeur sort du */
/*              domaine representable par 24 bits signes.                   */
/*==========================================================================*/

float	Player::get_max_clipping_level () const
{
	return (_max_clipping_level);
}



/*==========================================================================*/
/*      Nom: set_max_clipping_level                                         */
/*      Description: Fixe le niveau de clipping maximum. Il faut d'abord    */
/*                   avoir mesure le clipping courant et compare avec le    */
/*                   clipping maximum precedent.                            */
/*      Parametres en entree:                                               */
/*        - level: niveau de clipping de la piste (positif). Si la valeur   */
/*                 n'est pas representable sur 24 bits signes, la piste a   */
/*                 clippe.                                                  */
/*                 Une valeur de 0 sigale un reset du niveau                */
/*==========================================================================*/

void	Player::set_max_clipping_level (float level)
{
	_max_clipping_level = level;
}



/*==========================================================================*/
/*      Nom: get_bar_time                                                   */
/*      Description: Renvoie la mesure de la partition                      */
/*      Parametres en sortie:                                               */
/*        - high: nombre de temps dans une mesure.                          */
/*        - low: subdivision de la ronde donnant le temps (puissance de 2)  */
/*==========================================================================*/

void	Player::get_bar_time (int &high, int &low) const
{
	std::lock_guard <std::mutex>	lock (GTK_mutex);

	high = _score_info.time_high;
	low  = _score_info.time_low;
}



/*==========================================================================*/
/*      Nom: set_bar_time                                                   */
/*      Description: Fixe le temps de la mesure. Les parametres sont        */
/*                   changes que s'ils sont positifs.                       */
/*      Parametres en entree:                                               */
/*        - high: nombre de temps dans une mesure.                          */
/*        - low: subdivision de la ronde donnant le temps (doit etre une    */
/*               puissance de 2).                                           */
/*==========================================================================*/

void	Player::set_bar_time (int high, int low)
{
	std::lock_guard <std::mutex>	lock (GTK_mutex);

	if (high > 0)
	{
		_score_info.time_high = high;
	}
	if (low > 0)
	{
		_score_info.time_low = low;
	}
}



/*==========================================================================*/
/*      Nom: get_bar_length                                                 */
/*      Description: Donne le nombre de lignes par mesure.                  */
/*      Retour: nombre de lignes.                                           */
/*==========================================================================*/

int	Player::get_bar_length () const
{
	int		time_hi;
	int		time_lo;

	get_bar_time (time_hi, time_lo);

	return ((time_hi << 4) / time_lo);
}



/*==========================================================================*/
/*      Nom: get_fx_preset                                                  */
/*      Description: Memorise un effet d'une piste dans un preset.          */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste d'effet de laquelle on veut extraire  */
/*                 l'effet.                                                 */
/*        - preset: numero du preset d'effet dans lequel l'effet sera       */
/*                  memorise.                                               */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 si le player n'etait pas en marche,                       */
/*              2 si la piste n'existait pas,                               */
/*              3 si le preset n'existait pas.                              */
/*==========================================================================*/

int	Player::get_fx_preset (int track, int preset) const
{
	int		type;
	FxPreset_EFFECT_CONF	temp_conf;

	if (! _player_started_flag)
	{
		return (1);
	}
	else if (track >= GTK_nbr_tracks [Pattern_TYPE_FX])
	{
		return (2);
	}
	else if (preset > GTK_NBRFXP_TOTAL)
	{
		return (3);
	}

	std::lock_guard <std::mutex>	lock (GTK_mutex);

	fx_fx_get_preset (
		_track_info [_track_info_list [Pattern_TYPE_FX] [track]],
		temp_conf,
		type
	);
	FXP_set_effect_type (preset, type);
	FXP_set_parameters (preset, temp_conf);

	return (0);
}



/*==========================================================================*/
/*      Nom: set_fx_preset                                                  */
/*      Description: Applique un preset d'effet a une piste.                */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste d'effet sur laquelle on veut          */
/*                 appliquer l'effet.                                       */
/*        - preset: numero du preset d'effet a appliquer.                   */
/*        - 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 precedant.                */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 si le player n'etait pas en marche,                       */
/*              2 si la piste n'existait pas,                               */
/*==========================================================================*/

int	Player::set_fx_preset (int track, int preset, long auto_val)
{
	if (! _player_started_flag)
	{
		return (1);
	}
	else if (track >= GTK_nbr_tracks [Pattern_TYPE_FX])
	{
		return (2);
	}

	std::lock_guard <std::mutex>	lock (GTK_mutex);

	fx_fx_set_preset (
		_track_info [_track_info_list [Pattern_TYPE_FX] [track]],
		FXP_get_parameters (preset),
		FXP_get_effect_type (preset), auto_val
	);

	return (0);
}



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

bool	Player::get_bypass_flag (int track) const
{
	bool		bypass_flag;

	if (! _player_started_flag)
	{
		return (false);
	}
	else if (track >= GTK_nbr_tracks [Pattern_TYPE_FX])
	{
		return (false);
	}

	std::lock_guard <std::mutex>	lock (GTK_mutex);

	const Player::TrackInfo &	track_info =
		_track_info [_track_info_list [Pattern_TYPE_FX] [track]];
	bypass_flag = track_info.mix.fx.bypass_flag;
	
	return (bypass_flag);
}



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

void	Player::set_bypass_flag (int track, bool bypass_flag)
{
	if (! _player_started_flag)
	{
		return;
	}
	else if (track >= GTK_nbr_tracks [Pattern_TYPE_FX])
	{
		return;
	}

	std::lock_guard <std::mutex>	lock (GTK_mutex);

	Player::TrackInfo &	track_info =
		_track_info [_track_info_list [Pattern_TYPE_FX] [track]];
	track_info.mix.fx.bypass_flag = bypass_flag;
}



/*==========================================================================*/
/*      Nom: play_sound                                                     */
/*      Description: Joue un son sur une piste de sample donnee.            */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste sur laquelle on veut jouer le son.    */
/*        - sample: numero du sample a jouer (peut etre le sample           */
/*                  temporaire).                                            */
/*        - start_pos: position de depart de replay (en samples).           */
/*==========================================================================*/

void	Player::play_sound (int track, int sample, long start_pos)
{
	std::lock_guard <std::mutex>	lock (GTK_mutex);

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

	set_lin_velocity (track_info, 0x800);
	track_info.score.sample.number = sample;
	track_info.score.sample.transp = 0;
	track_info.score.sample.volume = 0x100;
	track_info.score.sample.finetune = 0;
	track_info.stereo = SAMP_get_sample_stereo (sample);

	track_info.score.note = Sample_REF_C2;
	track_info.score.porta.note = Sample_REF_C2;
	track_info.score.period = (float) convert_note_to_internal_period (Sample_REF_C2, 0);
	track_info.score.porta.period = track_info.score.period;

	const int		voice_index = cut_note (track_info);
	Voice &			voice = _voice_arr [voice_index];
	voice.start_sample (sample);
	voice.filter.deactivate ();

	voice._env_set.init_all_env (-1, track_info.track_type);

	voice.spl.set_playback_pos (start_pos, 0);
}



/*==========================================================================*/
/*      Nom: play_pattern_line_spl                                          */
/*      Description: Joue des notes sur certaines voies, a n'importe quel   */
/*                   moment. Les notes sont placees dans la variable        */
/*                   _new_spl_notes adequate et le flag                     */
/*                   _new_line_flag [???] sera remis a 0 au moment ou       */
/*                   la nouvelle ligne sera prise en compte, ainsi que les  */
/*                   _new_note flag [???] [] correspondants.                */
/*==========================================================================*/

void	Player::play_pattern_line_spl ()
{
	int					track;
	int					track_max;
	UBYTE					*note_ptr;
	int					*track_info_list_ptr;

	note_ptr = (UBYTE *)_new_spl_notes;
	track_max = GTK_nbr_tracks [Pattern_TYPE_SPL];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_SPL];
	for (track = 0; track < track_max; track ++)
	{
		TrackInfo	&	track_info = _track_info [*track_info_list_ptr++];
		if (_new_note_flag [Pattern_TYPE_SPL] [track] != 0)
		{
			track_info.score.score.note = *note_ptr;
			track_info.score.score.instr = *(note_ptr + 1);
			track_info.score.score.effect = BASE_dpeek_moto (note_ptr + 2);
			track_info.score.score.volume = *(note_ptr + 4);
			cmd_manage_effects_1st_tick_spl (track);
			env_manage_envelope (Pattern_TYPE_SPL, track);
			compute_final_parameters_track (Pattern_TYPE_SPL, track);
			_new_note_flag [Pattern_TYPE_SPL] [track] = 0;
		}
		note_ptr += MODS_GT2_SPL_NOTE_LEN;
	}

	_new_line_flag [Pattern_TYPE_SPL] = 0;
}



/*==========================================================================*/
/*      Nom: play_pattern_line_ain                                          */
/*      Description: Joue des notes sur certaines voies, a n'importe quel   */
/*                   moment. Les notes sont placees dans la variable        */
/*                   _new_ain_notes adequate et le flag                     */
/*                   _new_line_flag [???] sera remis a 0 au moment ou       */
/*                   la nouvelle ligne sera prise en compte, ainsi que les  */
/*                   _new_note flag [???] [] correspondants.                */
/*==========================================================================*/

void	Player::play_pattern_line_ain ()
{
	int					track;
	int					track_max;
	UBYTE					*note_ptr;
	int					*track_info_list_ptr;

	note_ptr = (UBYTE *)_new_ain_notes;
	track_max = GTK_nbr_tracks [Pattern_TYPE_AIN];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_AIN];
	for (track = 0; track < track_max; track ++)
	{
		TrackInfo	&	track_info = _track_info [*track_info_list_ptr++];
		if (_new_note_flag [Pattern_TYPE_AIN] [track] != 0)
		{
			track_info.score.score.effect = BASE_dpeek_moto (note_ptr);
			track_info.score.score.volume = *(note_ptr + 2);
			cmd_manage_effects_1st_tick_ain (track);
			env_manage_envelope (Pattern_TYPE_AIN, track);
			compute_final_parameters_track (Pattern_TYPE_AIN, track);
			_new_note_flag [Pattern_TYPE_AIN] [track] = 0;
		}
		note_ptr += MODS_GT2_AIN_NOTE_LEN;
	}

	_new_line_flag [Pattern_TYPE_AIN] = 0;
}



/*==========================================================================*/
/*      Nom: play_pattern_line_fx                                           */
/*      Description: Joue des notes sur certaines voies, a n'importe quel   */
/*                   moment. Les notes sont placees dans la variable        */
/*                   _new_fx_notes adequate et le flag                      */
/*                   _new_line_flag [???] sera remis a 0 au moment ou       */
/*                   la nouvelle ligne sera prise en compte, ainsi que les  */
/*                   _new_note flag [???] [] correspondants.                */
/*==========================================================================*/

void	Player::play_pattern_line_fx ()
{
	int					track;
	int					track_max;
	UBYTE					*note_ptr;
	int					*track_info_list_ptr;

	note_ptr = (UBYTE *)_new_fx_notes;
	track_max = GTK_nbr_tracks [Pattern_TYPE_FX];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_FX];
	for (track = 0; track < track_max; track ++)
	{
		TrackInfo &		track_info = _track_info [*track_info_list_ptr++];
		if (_new_note_flag [Pattern_TYPE_FX] [track] != 0)
		{
			track_info.score.score.fx_data = BASE_lpeek_moto (note_ptr);
			track_info.score.score.effect = BASE_dpeek_moto (note_ptr + 4);
			track_info.score.score.volume = *(note_ptr + 6);
			cmd_manage_effects_1st_tick_fx (track);
			env_manage_envelope (Pattern_TYPE_FX, track);
			compute_final_parameters_track (Pattern_TYPE_FX, track);
			_new_note_flag [Pattern_TYPE_FX] [track] = 0;
		}
		note_ptr += MODS_GT2_FX_NOTE_LEN;
	}

	_new_line_flag [Pattern_TYPE_FX] = 0;
}



/*==========================================================================*/
/*      Nom: play_pattern_line_mid                                          */
/*      Description: Joue des notes sur certaines voies, a n'importe quel   */
/*                   moment. Les notes sont placees dans la variable        */
/*                   _new_mid_notes adequate et le flag                     */
/*                   _new_line_flag [???] sera remis a 0 au moment ou       */
/*                   la nouvelle ligne sera prise en compte, ainsi que les  */
/*                   _new_note flag [???] [] correspondants.                */
/*==========================================================================*/

void	Player::play_pattern_line_mid ()
{
	int					track;
	int					track_max;
	UBYTE					*note_ptr;
	int					*track_info_list_ptr;

	note_ptr = (UBYTE *)_new_ain_notes;
	track_max = GTK_nbr_tracks [Pattern_TYPE_MID];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_MID];
	for (track = 0; track < track_max; track ++)
	{
		TrackInfo &		track_info = _track_info [*track_info_list_ptr++];
		if (_new_note_flag [Pattern_TYPE_MID] [track] != 0)
		{
			track_info.score.score.mid_data = BASE_lpeek_moto (note_ptr);
			cmd_manage_effects_1st_tick_mid (track);
			_new_note_flag [Pattern_TYPE_MID] [track] = 0;
		}
		note_ptr += MODS_GT2_MID_NOTE_LEN;
	}

	_new_line_flag [Pattern_TYPE_MID] = 0;
}



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

bool	Player::get_linear_period_flag () const
{
	return (_score_info.linear_period_flag);
}



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

void	Player::set_linear_period_flag (bool flag)
{
	std::lock_guard <std::mutex>	lock (GTK_mutex);

	_score_info.linear_period_flag = flag;
}



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



/*==========================================================================*/
/*      Nom: convert_freq_to_note                                           */
/*      Description: Convertit une frequence en une note midi + finetune.   */
/*      Parametres en entree:                                               */
/*        - freq: frequence (en Hz) a convertir en note.                    */
/*      Parametres en sortie:                                               */
/*        - finetune: erreur sur la conversion de la note, en 1/2 tons.     */
/*                    Toujours dans [-0.5; 0.5[.                            */
/*      Retour: La note au format MIDI. Le C2 de ref donne 48 et le La 440  */
/*              donne 69. La note peut sortir des intervales MIDI si la     */
/*              frequence est tres haute ou tres basse.                     */
/*==========================================================================*/

int	Player::convert_freq_to_note (double freq, double &finetune) const
{
	int	note;
	double	real_note;

	real_note = log (freq / 440.0) * (12.0 / LN2) + Sample_REF_A440;
	note = (int) floor (real_note + 0.5);
	finetune = real_note - note;

	return (note);
}



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

double	Player::convert_note_to_freq (int note, double finetune) const
{
	return (440.0 * pow (2.0, (note + finetune - Sample_REF_A440) * (1.0 / 12.0)));
}



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

/* per interne -> freq */
double	Player::convert_period_to_freq (float period) const
{
	if (get_linear_period_flag ())
	{
		return (440.0 * pow (2.0,   (  (double)Player::REF_LIN_PERIOD - period
		                             + ((Sample_REF_C2 - Sample_REF_A440) * Player::LIN_PER_NOTE))
		                          * (1.0 / (12*Player::LIN_PER_NOTE))));
	}
	else
	{
		return (  (Player::REF_PERIOD * convert_note_to_freq (Sample_REF_C2, 0))	/*** A mettre en constante ***/
		        / (double)period);
	}
}


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

/* Rel freq -> per interne */
double	Player::convert_rel_freq_to_period (double freq) const
{
	if (get_linear_period_flag ())
	{
		return (Player::REF_LIN_PERIOD - log (freq) * ((12*Player::LIN_PER_NOTE) / LN2));
	}
	else
	{
		return (Player::REF_PERIOD / freq);
	}
}



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

/* per interne -> rel freq */
double	Player::convert_period_to_rel_freq (float period) const
{
	if (get_linear_period_flag ())
	{
		return (pow (2.0, ((double)Player::REF_LIN_PERIOD - period) * (1.0 / (12*Player::LIN_PER_NOTE))));
	}
	else
	{
		return ((double)Player::REF_PERIOD / period);
	}
}



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

/* per interne -> Rel per */
double	Player::convert_period_to_rel_per (float period) const
{
	if (get_linear_period_flag ())
	{
		return (pow (2.0, (period - (double)Player::REF_LIN_PERIOD) * (1.0 / (12*Player::LIN_PER_NOTE))));
	}
	else
	{
		return (period / (double)Player::REF_PERIOD);
	}
}



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

/* per interne -> per */
double	Player::convert_period_to_time (float period) const
{
	if (get_linear_period_flag ())
	{
		return (  (1.0 / 440.0)
		        * pow (2.0,   (  period - (double)Player::REF_LIN_PERIOD
		                       + ((Sample_REF_A440 - Sample_REF_C2) * Player::LIN_PER_NOTE))
		                    * (1.0 / (12*Player::LIN_PER_NOTE))));
	}
	else
	{
		return (  (double)period  
		        / (Player::REF_PERIOD * convert_note_to_freq (Sample_REF_C2, 0)));
	}
}


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

double	Player::convert_note_to_internal_period (int note, int finetune) const
{
	if (get_linear_period_flag ())
	{
		return (Player::REF_LIN_PERIOD - ((note - Sample_REF_C2) * Player::LIN_PER_NOTE) - (finetune * (Player::LIN_PER_NOTE >> 3)));
	}
	else
	{
		return (_note_period_table_ref [(note << 3) + finetune]);
	}
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



void	Player::do_run_thread ()
{
	int		track;
	float		clip_level;
	float		clip_level_array [GTK_NBROUT_MAXI] = { 0 };
	QWORD		tick_start_time = 0;
	QWORD		tick_duration = 0;
	QWORD		current_time;
	Thread_Message	message;

	/* On se fixe le niveau de priorite le plus haut */
	if (_player_thread.set_priority (Thread_PRIORITY_TIME_CRITICAL))
	{
		LOG_printf ("player_thread_routine: Warning: couldn't set priority for player thread.\n");
	}

	if (OS_sound_driver.start_replay ())
	{
		LOG_printf ("player_thread_routine: Error: couldn't start player.\n");
		_player_thread_state = -1;
		return;
	}

	_player_thread_state = 1;

	tick_start_time = OS_get_time ();
	message = Thread_MESSAGE_NONE;
	do
	{
		/* Gere la partition et fixe une nouvelle longueur de frame */
		main_interrupt ();

		/* Fin du chronometrage du tick. */
		tick_duration = OS_get_time () - tick_start_time;

		/* Signale au driver la nouvelle frame et attend de pouvoir
			transferer les donnees. */
		OS_sound_driver.set_new_frame (MIX_frame_length);

		/* Calcul du temps machine utilise (debut du chrono) */
		current_time = OS_get_time ();
		if (current_time - tick_start_time > 0)
		{
			OS_cpu_time = (int)(((double)tick_duration * 100) / (current_time - tick_start_time));
		}
		tick_start_time = current_time;

		/* Recupere les donnees en entree */
		OS_sound_driver.get_data (MIX_driver_in_buffer_ptr);

		/* Effectue le mixage */
		MIX_do_mix ();

		/* Expedie au driver les donnees resultat */
		OS_sound_driver.set_data (MIX_driver_out_buffer_ptr, clip_level_array);

		/* Recupere les informations de saturation */
		clip_level = get_max_clipping_level ();
		for (track = 0; track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; track ++)
		{
			Player::set_clipping_level (track, clip_level_array [track]);
			clip_level = MAX (clip_level, clip_level_array [track]);
		}
		set_max_clipping_level (clip_level);

		/* Regarde si on nous demande d'arreter le replay */
		message = _player_thread.get_message ();
	}
	while (message != Thread_MESSAGE_QUIT);

	OS_sound_driver.stop_replay ();

	_player_thread_state = 0;
}



// Table d'onde de periode 64. -1 = -0xFF, 1 = 0xFF
const SWORD	Player::_sin_table [4] [64] =
{
	// Sinus
	{
		 0x00,  0x18,  0x31,  0x4A,  0x61,  0x78,  0x8D,  0xA1,
		 0xB4,  0xC5,  0xD4,  0xE0,  0xEB,  0xF4,  0xFA,  0xFD,
		 0xFF,  0xFD,  0xFA,  0xF4,  0xEB,  0xE0,  0xD4,  0xC5,
		 0xB4,  0xA1,  0x8D,  0x78,  0x61,  0x4A,  0x31,  0x18,
		-0x00, -0x18, -0x31, -0x4A, -0x61, -0x78, -0x8D, -0xA1,
		-0xB4, -0xC5, -0xD4, -0xE0, -0xEB, -0xF4, -0xFA, -0xFD,
		-0xFF, -0xFD, -0xFA, -0xF4, -0xEB, -0xE0, -0xD4, -0xC5,
		-0xB4, -0xA1, -0x8D, -0x78, -0x61, -0x4A, -0x31, -0x18
	},

	// Square
	{
		 0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,
		 0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,
		 0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,
		 0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,
		-0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF,
		-0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF,
		-0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF,
		-0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF, -0xFF
	},

	// Rampdown
	{
		 0xFF,  0xF8,  0xF0,  0xE8,  0xE0,  0xD8,  0xD0,  0xC8,
		 0xC0,  0xB8,  0xB0,  0xA8,  0xA0,  0x98,  0x90,  0x88,
		 0x80,  0x78,  0x70,  0x68,  0x60,  0x58,  0x50,  0x48,
		 0x40,  0x38,  0x30,  0x28,  0x20,  0x18,  0x10,  0x08,
		-0x00, -0x08, -0x10, -0x18, -0x20, -0x28, -0x30, -0x38,
		-0x40, -0x48, -0x50, -0x58, -0x60, -0x68, -0x70, -0x78,
		-0x80, -0x88, -0x90, -0x98, -0xA0, -0xA8, -0xB0, -0xB8,
		-0xC0, -0xC8, -0xD0, -0xD8, -0xE0, -0xE8, -0xF0, -0xF8
	},

	// Rampup
	{
		-0xF8, -0xF0, -0xE8, -0xE0, -0xD8, -0xD0, -0xC8, -0xC0,
		-0xB8, -0xB0, -0xA8, -0xA0, -0x98, -0x90, -0x88, -0x80,
		-0x78, -0x70, -0x68, -0x60, -0x58, -0x50, -0x48, -0x40,
		-0x38, -0x30, -0x28, -0x20, -0x18, -0x10, -0x08,  0x00,
		 0x08,  0x10,  0x18,  0x20,  0x28,  0x30,  0x38,  0x40,
		 0x48,  0x50,  0x58,  0x60,  0x68,  0x70,  0x78,  0x80,
		 0x88,  0x90,  0x98,  0xA0,  0xA8,  0xB0,  0xB8,  0xC0,
		 0xC8,  0xD0,  0xD8,  0xE0,  0xE8,  0xF0,  0xF8,  0xFF
	}
};



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



Player::Player ()
// Public
:/*	_track_info_list ()
,	_track_info ()
,*/	_voice_arr ()
,	_score_info ()
/*,	_new_spl_notes ()
,	_new_ain_notes ()
,	_new_fx_notes ()
,	_new_mid_notes ()
,	_new_line_flag ()
,	_new_note_flag ()*/
// Private
,	_player_thread (*this)
,	_player_thread_state (0)
,	_player_started_flag (false)
,	_current_linepos (0)
,	_current_songpos (0)
,	_new_play_mode (MODE_STOP)
,	_new_play_mode_flag (false)
,	_new_play_mode_cont_flag (false)
,	_new_linepos (0)
,	_new_songpos (0)
/*,	_clipping_level ()*/
,	_max_clipping_level (0)
,	_elapsed_time (0)
,	_default_interpolation_flag (true)
,	_free_voice_stack ()
{
	using namespace std;

	memset ((void *) &_new_spl_notes, 0, sizeof (_new_spl_notes));
	memset ((void *) &_new_ain_notes, 0, sizeof (_new_ain_notes));
	memset ((void *) &_new_fx_notes , 0, sizeof (_new_fx_notes));
	memset ((void *) &_new_mid_notes, 0, sizeof (_new_mid_notes));
	memset ((void *) &_new_line_flag, 0, sizeof (_new_line_flag));
	memset ((void *) &_new_note_flag, 0, sizeof (_new_note_flag));

	memset ((void *) _clipping_level, 0, sizeof (_clipping_level));

	_free_voice_stack.reserve (MAX_NBR_VOICES);

	init_constants ();
}



int	Player::set_replay_format ()
{
	SoundDriver_CONFIG	config;
	int		out_stereo [GTK_NBROUT_MAXI];
	int		in_stereo [GTK_NBRIN_MAXI];
	int		track;
	int		global_track_nbr;

	config.sample_freq = 44100;
	config.nbr_buffers = 2;
	config.nbr_out = GTK_nbr_tracks [GTK_TRACK_TYPE_AOU];
	config.nbr_in = GTK_nbr_tracks [Pattern_TYPE_AIN];
	config.out_stereo_ptr = out_stereo;
	config.in_stereo_ptr = in_stereo;
	for (track = 0; track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; track ++)
	{
		global_track_nbr = _track_info_list [GTK_TRACK_TYPE_AOU] [track];
		Player::TrackInfo &	track_info = _track_info [global_track_nbr];
		out_stereo [track] = track_info.stereo;
	}
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_AIN]; track ++)
	{
		global_track_nbr = _track_info_list [Pattern_TYPE_AIN] [track];
		Player::TrackInfo &	track_info = _track_info [global_track_nbr];
		out_stereo [track] = track_info.stereo;
	}

	/* Fait evaluer la config */
	if (OS_sound_driver.set_config (&config))
	{
		LOG_printf ("set_replay_format: Error: couldn't set configuration.\n");
		return (-1);
	}

	/* Reajuste les config des pistes en fonction de la config
		reelle renvoyee par le driver. */

	/*** A faire ***/

	/* Fabrique les buffers de mixage */
	MIX_create_mix_buffers ();

	return (0);
}



/*==========================================================================*/
/*      Nom: copy_line_to_track_info                                        */
/*      Description: Copie la ligne courante du pattern courant dans les    */
/*                   blocs d'informations sur les pistes.                   */
/*      Parametres en entree:                                               */
/*        - score_info_ptr: pointeur sur les info de la partition.          */
/*==========================================================================*/

void	Player::copy_line_to_track_info ()
{
	int					track;
	int					track_max;
	UBYTE					*note_ptr;
	int					*track_info_list_ptr;

	/* Samples */
	note_ptr = (UBYTE *) PAT_get_spl_note_adr_pat (_score_info.current_pattern,
	                                               _score_info.current_line, 0);
	track_max = GTK_nbr_tracks [Pattern_TYPE_SPL];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_SPL];
	for (track = 0; track < track_max; track ++)
	{
		Player::TrackInfo &	track_info = _track_info [*track_info_list_ptr++];
		track_info.score.score.note = *note_ptr;
		track_info.score.score.instr = *(note_ptr + 1);
		track_info.score.score.effect = BASE_dpeek_moto (note_ptr + 2);
		track_info.score.score.volume = *(note_ptr + 4);
		note_ptr += MODS_GT2_SPL_NOTE_LEN;
	}

	/* Audio in */
	note_ptr = (UBYTE *) PAT_get_ain_note_adr_pat (_score_info.current_pattern,
	                                               _score_info.current_line, 0);
	track_max = GTK_nbr_tracks [Pattern_TYPE_AIN];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_AIN];
	for (track = 0; track < track_max; track ++)
	{
		Player::TrackInfo &	track_info = _track_info [*track_info_list_ptr++];
		track_info.score.score.effect = BASE_dpeek_moto (note_ptr);
		track_info.score.score.volume = *(note_ptr + 2);
		note_ptr += MODS_GT2_AIN_NOTE_LEN;
	}

	/* Effets */
	note_ptr = (UBYTE *) PAT_get_fx_note_adr_pat (_score_info.current_pattern,
	                                              _score_info.current_line, 0);
	track_max = GTK_nbr_tracks [Pattern_TYPE_FX];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_FX];
	for (track = 0; track < track_max; track ++)
	{
		Player::TrackInfo &	track_info = _track_info [*track_info_list_ptr++];
		track_info.score.score.fx_data = BASE_lpeek_moto (note_ptr);
		track_info.score.score.effect = BASE_dpeek_moto (note_ptr + 4);
		track_info.score.score.volume = *(note_ptr + 6);
		note_ptr += MODS_GT2_FX_NOTE_LEN;
	}

	/* MIDI */
	note_ptr = (UBYTE *) PAT_get_mid_note_adr_pat (_score_info.current_pattern,
	                                               _score_info.current_line, 0);
	track_max = GTK_nbr_tracks [Pattern_TYPE_MID];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_MID];
	for (track = 0; track < track_max; track ++)
	{
		Player::TrackInfo &	track_info = _track_info [*track_info_list_ptr++];
		track_info.score.score.mid_data = BASE_lpeek_moto (note_ptr);
		note_ptr += MODS_GT2_MID_NOTE_LEN;
	}
}



/*==========================================================================*/
/*      Nom: handle_effects_1st_tick												    */
/*      Description: Gere les effets, notes et parametres qui n'agissent    */
/*                   qu'au premier tick d'une ligne.                        */
/*==========================================================================*/

void	Player::handle_effects_1st_tick ()
{
	int							track;
	int							track_max;

	/* Samples */
	track_max = GTK_nbr_tracks [Pattern_TYPE_SPL];
	for (track = 0; track < track_max; track ++)
	{
		cmd_manage_effects_1st_tick_spl (track);
	}

	/* Audio In */
	track_max = GTK_nbr_tracks [Pattern_TYPE_AIN];
	for (track = 0; track < track_max; track ++)
	{
		cmd_manage_effects_1st_tick_ain (track);
	}

	/* Effets */
	track_max = GTK_nbr_tracks [Pattern_TYPE_FX];
	for (track = 0; track < track_max; track ++)
	{
		cmd_manage_effects_1st_tick_fx (track);
	}

	/* MIDI */
	track_max = GTK_nbr_tracks [Pattern_TYPE_MID];
	for (track = 0; track < track_max; track ++)
	{
		cmd_manage_effects_1st_tick_mid (track);
	}
}



/*==========================================================================*/
/*      Nom: handle_effects_all_ticks                                       */
/*      Description: Gere les effets qui agissent pendant toute la duree de */
/*                   le ligne.                                              */
/*==========================================================================*/

void	Player::handle_effects_all_ticks ()
{
	int							track;
	int							track_max;

	/* Samples */
	track_max = GTK_nbr_tracks [Pattern_TYPE_SPL];
	for (track = 0; track < track_max; track ++)
	{
		cmd_manage_effects_all_ticks_spl (track);
	}

	/* Audio In */
	track_max = GTK_nbr_tracks [Pattern_TYPE_AIN];
	for (track = 0; track < track_max; track ++)
	{
		cmd_manage_effects_all_ticks_ain (track);
	}

	/* Effets */
	track_max = GTK_nbr_tracks [Pattern_TYPE_FX];
	for (track = 0; track < track_max; track ++)
	{
		cmd_manage_effects_all_ticks_fx (track);
	}

	/* MIDI */
	track_max = GTK_nbr_tracks [Pattern_TYPE_MID];
	for (track = 0; track < track_max; track ++)
	{
		cmd_manage_effects_all_ticks_mid (track);
	}
}



/*==========================================================================*/
/*      Nom: compute_final_parameters                                       */
/*      Description: Calcule les parametres de mixage finaux a partir des   */
/*                   donnees fournies par chaque piste.                     */
/*==========================================================================*/

void	Player::compute_final_parameters ()
{
	int							track;
	int							track_max;

	/* Samples */
	track_max = GTK_nbr_tracks [Pattern_TYPE_SPL];
	for (track = 0; track < track_max; track ++)
	{
		compute_final_parameters_track (Pattern_TYPE_SPL, track);
	}

	/* Audio In */
	track_max = GTK_nbr_tracks [Pattern_TYPE_AIN];
	for (track = 0; track < track_max; track ++)
	{
		compute_final_parameters_track (Pattern_TYPE_AIN, track);
	}

	/* Effets */
	track_max = GTK_nbr_tracks [Pattern_TYPE_FX];
	for (track = 0; track < track_max; track ++)
	{
		compute_final_parameters_track (Pattern_TYPE_FX, track);
	}
}



/*==========================================================================*/
/*      Nom: compute_final_parameters_track                                 */
/*      Description: Calcule les parametres de mixage finaux d'une piste a  */
/*                   partir des donnees de la partition.                    */
/*      Parametres en entree:                                               */
/*        - track_type: type de la piste.                                   */
/*        - track: numero de la piste a calculer.                           */
/*==========================================================================*/

void	Player::compute_final_parameters_track (int track_type, int track)
{
	TrackInfo &		track_info =
		_track_info [_track_info_list [track_type] [track]];

	if (   track_type == Pattern_TYPE_SPL
	    || track_type == Pattern_TYPE_AIN
		 || track_type == Pattern_TYPE_FX)
	{

/*______________________________________________
 *
 * Volume
 *______________________________________________
 */

		if (track_info.score.changed_volume_flag)
		{
			long				volume_base = track_info.score.sample.volume;	// 0...100...FFF
			volume_base *= track_info.score.velocity_loc;	// 0...80000...7FF800

			// We update only the volume of the main active voice.
			// Otherwise, a muted sample could suddenly appear again at the next
			// triggered note, then fade out quickly; this would obviously make
			// a click.
//			for (int voice_cnt = 0; voice_cnt < track_info.mix.spl.nbr_voices; ++voice_cnt)
			int	voice_cnt = 0;	if (voice_cnt < track_info.mix.spl.nbr_voices)
			{
				const int		voice_index = track_info.mix.spl.voice_index_arr [voice_cnt];
				Voice &			voice = _voice_arr [voice_index];

				long				volume = volume_base;	// 0...80000...7FF800
				if (voice._env_set._com_env [Envelope_TYPE_VOL].nbr != 0)
				{
					volume >>= 8;	// 0...800...7FF8
					volume *= voice._env_set._com_env [Envelope_TYPE_VOL].proc.final_val;	// 0...800000...3FFB8008
					volume >>= 15;	// 0...100...7FF7
					volume = MIN (volume, 0xFFF);	// 0...100...FFF
				}
				else
				{
					volume >>= 11;	// 0...100...FFF
				}

				volume *= track_info.score.track.volume_lin >> 4;

				track_info.outvol = UWORD (volume >> 4);
				voice._outvol     = int (volume);
			}
		}

		int				nbr_chn = 1;
		long				last_pan = -1;
		for (int voice_cnt = 0; voice_cnt < track_info.mix.spl.nbr_voices; ++voice_cnt)
		{
			const int		voice_index = track_info.mix.spl.voice_index_arr [voice_cnt];
			Voice &			voice = _voice_arr [voice_index];

/*______________________________________________
 *
 * Balance
 *______________________________________________
 */

			long				panning = track_info.score.track.panning;	// 0...8000...FFFF
			if (voice._env_set._com_env [Envelope_TYPE_PAN].nbr != 0)
			{
				long				temp = panning;
				if (temp > 0x8000)
				{
					temp = 0x10000 - temp;	// 0...8000
				}
				long				pan_env =
					voice._env_set._com_env [Envelope_TYPE_PAN].proc.final_val;	// -80...0...7F
				pan_env *= temp;		// -400000...0...3FFF80
				pan_env /= 0x80;		// -8000...0...8000
				panning += pan_env;	// 0...8000...10000
				panning = MIN (panning, 0xFFFF);
			}

			track_info.outpan = UWORD (panning);
			voice._outpan     = int (panning);

			// If we have several mono voices with different panning positions,
			// we convert everything in stereo.
			if (nbr_chn == 1 && voice_cnt > 0 && panning != last_pan)
			{
				nbr_chn = 2;
			}
			last_pan = panning;

/*______________________________________________
 *
 * Periode
 *______________________________________________
 */

			if (   track_type == Pattern_TYPE_SPL
				 || track_type == Pattern_TYPE_FX)
			{
				float				period = track_info.score.period_loc;
				if (voice._env_set._com_env [Envelope_TYPE_TON].nbr != 0)
				{
					if (get_linear_period_flag ())
					{
						period -= float (
							  voice._env_set._com_env [Envelope_TYPE_TON].proc.final_val
							* Player::LIN_PER_NOTE
							* 0.01);
					}
					else
					{
						period = float (
							period * exp (
								  voice._env_set._com_env [Envelope_TYPE_TON].proc.final_val
								* (-LN2 / 1200)
							)
						);
					}
				}
				period = MIN (period, Player::MAX_PERIOD);
				period = MAX (period, Player::MIN_PERIOD);

				voice._outperiod = period;

				if (track_type == Pattern_TYPE_SPL)
				{
					const double	rel_per = convert_period_to_rel_per (period);
					const double	rate = voice.spl.freq / (rel_per * MIX_replay_freq);

					voice.spl.set_rate (rate);

					// Updates the maximum number of channesl
					nbr_chn = MAX (nbr_chn, voice.spl.tracks);

/*______________________________________________
 *
 * Filtre passe-bas
 *______________________________________________
 */

					if (track_info.score.lpf.filter_flag)
					{
						const double	lerp_velo =
							1 - track_info.score.velocity_loc * (1.0 / 0x800);
						if (track_info.score.lpf.freq_vol_flag)
						{
							voice.filter.set_lerp_freq (lerp_velo);
						}
						if (track_info.score.lpf.q_vol_flag)
						{
							voice.filter.set_lerp_q (lerp_velo);
						}

						const double	note_freq = convert_period_to_freq (period);
						voice.filter.compute_final_data (note_freq, voice._env_set);
					}
				}
			}
		}

/*______________________________________________
 *
 * Stereo (pour les pistes de sample)
 *______________________________________________
 */

		if (track_type == Pattern_TYPE_SPL)
		{
			track_info.stereo = nbr_chn;
		}

/*______________________________________________
 *
 * Effets (pour les pistes d'effet)
 *______________________________________________
 */

		else if (track_type == Pattern_TYPE_FX)
		{
			fx_manage_all_ticks (track_info);
		}
	}
}



/*==========================================================================*/
/*      Nom: reset_all_tracks                                               */
/*      Description: Remet toutes les voies a 0 et coupe toute source       */
/*                   sonore. La configuration des pistes d'effet est        */
/*                   toutefois gardee.                                      */
/*==========================================================================*/

void	Player::reset_all_tracks ()
{
	int					track;
	int					track_max;
	int					*track_info_list_ptr;

/*______________________________________________
 *
 * Samples
 *______________________________________________
 */

	track_max = GTK_nbr_tracks [Pattern_TYPE_SPL];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_SPL];
	for (track = 0; track < track_max; track ++)
	{
		TrackInfo &		track_info = _track_info [*track_info_list_ptr++];

		assert (track_info.mix.spl.nbr_voices > 0);
		for (int voice_cnt = 0; voice_cnt < track_info.mix.spl.nbr_voices; ++voice_cnt)
		{
			const int		voice_index = track_info.mix.spl.voice_index_arr [voice_cnt];
			Voice &			voice = _voice_arr [voice_index];

			/* Parametres de mixage */
			voice.deactivate ();

			/* Filtres de reconstruction */
			if (voice.spl.recons_filter_ptr != 0)
			{
				voice.spl.recons_filter_ptr->clear_buffer ();
			}

			if (voice_cnt > 0)
			{
				deallocate_voice (voice_index);
			}
		}
		track_info.mix.spl.nbr_voices = 1;

		/* Parametres de partition */
		reset_track_partition_info (track_info);
	}

/*______________________________________________
 *
 * Audio In
 *______________________________________________
 */

	track_max = GTK_nbr_tracks [Pattern_TYPE_AIN];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_AIN];
	for (track = 0; track < track_max; track ++)
	{
		TrackInfo &	track_info = _track_info [*track_info_list_ptr++];
		reset_track_partition_info (track_info);
	}

/*______________________________________________
 *
 * Effets
 *______________________________________________
 */

	track_max = GTK_nbr_tracks [Pattern_TYPE_FX];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_FX];
	for (track = 0; track < track_max; track ++)
	{
		TrackInfo &	track_info = _track_info [*track_info_list_ptr++];

		/* Parametres de partition */
		reset_track_partition_info (track_info);

		if (   track_info.mix.fx.effect != FxPreset_TYPE_NONE
		    && ! track_info.mix.fx.bad_init_flag)
		{
			/* Vide les buffers pour que l'effet ne produise que du silence
				avec une entree nulle. */
			fx_fx_silence (track_info);
		}
	}

/*______________________________________________
 *
 * MIDI
 *______________________________________________
 */

	track_max = GTK_nbr_tracks [Pattern_TYPE_MID];
	track_info_list_ptr = _track_info_list [Pattern_TYPE_MID];
	for (track = 0; track < track_max; track ++)
	{
		TrackInfo &	track_info = _track_info [*track_info_list_ptr++];

		reset_track_partition_info (track_info);
	}
}



/*==========================================================================*/
/*      Nom: reset_track_partition_info                                     */
/*      Description: Met a 0 la partie partition d'une piste.               */
/*==========================================================================*/

void	Player::reset_track_partition_info (TrackInfo &track_info)
{
	int		env_type;
	int		cnt;

	track_info.score.score.note = 0;
	track_info.score.score.instr = 0;
	track_info.score.score.effect = 0;
	track_info.score.score.effect_par = 0;
	track_info.score.score.volume = 0;
	track_info.score.score.fx_data = 0;
	track_info.score.score.fx_par = 0;
	track_info.score.score.mid_data = 0;
	track_info.score.instr = 0;
	track_info.score.note = Sample_REF_C2;
	set_lin_velocity (track_info, 0x800);
	track_info.score.velocity_loc = track_info.score.velocity_lin;
	track_info.score.period = (float) convert_note_to_internal_period (Sample_REF_C2, 0);
	track_info.score.changed_volume_flag = true;
	track_info.score.track.lin_vol_speed = 0;
	track_info.score.track.lin_vol_fine_speed = 0;
	track_info.score.track.log_vol_speed = 0;
	track_info.score.track.log_vol_fine_speed = 0;
	track_info.score.sample.number = 0;
	track_info.score.sample.volume = 0x100;
	track_info.score.sample.transp = 0;
	track_info.score.sample.finetune = 0;
	track_info.score.offset.pos_int = 0;
	track_info.score.offset.pos_frac = 0;
	track_info.score.arpeggio_cnt = 0;
	track_info.score.porta.note = Sample_REF_C2;
	track_info.score.porta.period = (float) convert_note_to_internal_period (Sample_REF_C2, 0);
	track_info.score.porta.speed = 0;
	track_info.score.porta.fine_speed = 0;
	track_info.score.vibrato.speed = 0;
	track_info.score.vibrato.cnt = 0;
	track_info.score.vibrato.amp = 0;
	track_info.score.vibrato.waveform = 0;
	track_info.score.tremolo.speed = 0;
	track_info.score.tremolo.cnt = 0;
	track_info.score.tremolo.amp = 0;
	track_info.score.tremolo.waveform = 0;
	track_info.score.tremor.cnt = 0;
	track_info.score.tremor.on = 3;
	track_info.score.tremor.period = 6;
	track_info.score.roll.speed = 3;
	track_info.score.roll.cnt = 0;
	track_info.score.roll.nbr = 0;
	track_info.score.delay.play = 0;
	track_info.score.delay.cut = 0;
	track_info.score.ploop.songpos = -1;
	track_info.score.ploop.linepos = 0;
	track_info.score.ploop.cnt = 0;
	track_info.score.vol_slide.speed = 0;
	track_info.score.vol_slide.fine_speed = 0;
	track_info.score.pan_slide.speed = 0;
	track_info.score.lpf.filter_flag = false;
	track_info.score.lpf.freq_vol_flag = false;
	track_info.score.lpf.q_vol_flag = false;
	track_info.score.lpf.freq [0] = 10000.0;
	track_info.score.lpf.freq [1] = 1000.0;
	track_info.score.lpf.q [0] = 0.5;
	track_info.score.lpf.q [1] = 0.5;

	const int		voice_index = track_info.mix.spl.voice_index_arr [0];
	Voice &			voice = _voice_arr [voice_index];
	EnvSet &			env_set = voice._env_set;
	for (env_type = 0; env_type < Envelope_NBR_TYPES; env_type ++)
	{
		env_set._com_env [env_type].nbr = 0;
	}

	for (cnt = 0; cnt < Player::FX_NBR_TIME_SPEED; cnt ++)
	{
		track_info.score.fx.time [cnt].speed = 0.0;
		track_info.score.fx.time [cnt].time = 1.0;
	}

	for (cnt = 0; cnt < Player::FX_NBR_FREQ_SPEED; cnt ++)
	{
		track_info.score.fx.freq [cnt].speed = 0.0;
		track_info.score.fx.freq [cnt].freq = 440.0;
	}
}



/*==========================================================================*/
/*      Nom: set_nbr_source_tracks                                          */
/*      Description: Fixe le nouveau nombre de pistes sources d'une piste.  */
/*                   Les configurations doivent etre mises a jour           */
/*                   separement.                                            */
/*      Parametres en entree:                                               */
/*        - track_type: Type de la piste.                                   */
/*        - track_nbr: Numero de la piste.                                  S*/
/*        - nbr_src: nouveau nombre de pistes sources > 0.                  */
/*      Retour: 0 si tout s'est bien passe, ou -1 si le type de la piste ne */
/*              correspondait pas a un type utilisant des pistes sources.   */
/*==========================================================================*/

int	Player::set_nbr_source_tracks (int track_type, int track_nbr, int nbr_src)
{
	TrackInfo &		track_info =
		_track_info [_track_info_list [track_type] [track_nbr]];

	if (track_type == GTK_TRACK_TYPE_AOU)
	{
		track_info.mix.src.nbr_s_tracks = nbr_src;
	}
	else if (track_type == Pattern_TYPE_FX)
	{
		track_info.mix.src.nbr_s_tracks = nbr_src;
	}
	else
	{
		/* Mauvais type destination */
		assert (false);
		return (-1);
	}

	return (0);
}



// Returns -1 if no voice is available
int	Player::allocate_voice ()
{
	int				alloc_index = -1;
	if (! _free_voice_stack.empty ())
	{
		alloc_index = _free_voice_stack.back ();
		_free_voice_stack.pop_back ();
	}

	return (alloc_index);
}



void	Player::deallocate_voice (int voice_index)
{
	assert (voice_index >= 0);
	assert (voice_index < MAX_NBR_VOICES);
	assert (std::find (_free_voice_stack.begin (), _free_voice_stack.end (), voice_index) == _free_voice_stack.end ());

	_free_voice_stack.push_back (voice_index);
}



// Returns the new voice_index
int	Player::cut_note (TrackInfo &track_info)
{
	assert (&track_info != 0);

	return (cut_or_dup_note (track_info, false));
}



int	Player::retrig_note (TrackInfo &track_info)
{
	assert (&track_info != 0);

	const int		voice_index_new = cut_or_dup_note (track_info, true);
	Voice &			voice_new = _voice_arr [voice_index_new];

	voice_new.spl.set_playback_pos (0, 0);
	voice_new.spl.set_direction (false);

	return (voice_index_new);
}



int	Player::cut_or_dup_note (TrackInfo &track_info, bool dup_flag)
{
	assert (&track_info != 0);

	const int		voice_index_cut = track_info.mix.spl.voice_index_arr [0];
	int				voice_index_new = voice_index_cut;
	Voice &			voice_cut = _voice_arr [voice_index_cut];

	if (voice_cut.has_generated_something ())
	{
		// If we have enough ghost tracks
		int				nbr_voices = track_info.mix.spl.nbr_voices;
		if (nbr_voices < TrackInfoSpl::MAX_GHOST_VOICES)
		{
			// Try to allocate a new voice
			const int		voice_index_tmp = allocate_voice ();

			// OK: set the current voice as ghost and use the new voice as main voice.
			if (voice_index_tmp >= 0)
			{
				voice_index_new = voice_index_tmp;

				track_info.mix.spl.voice_index_arr [nbr_voices] = voice_index_cut;
				track_info.mix.spl.voice_index_arr [0         ] = voice_index_new;
				++ nbr_voices;
				track_info.mix.spl.nbr_voices = nbr_voices;

				Voice &			voice_new = _voice_arr [voice_index_new];
				if (dup_flag)
				{
					voice_new.copy_state (voice_cut);
				}
				else
				{
					const bool		hq_flag = voice_cut.spl.has_hq_interpolator ();
					voice_new.spl.set_hq_interpolator (hq_flag);
					voice_new.deactivate ();
				}

				// This flag is more a track flag than a voice flag, it's not
				// set by the note playback functions, so we have to set it here.
				// It would me more appropriate to have a related flag in the
				// score information and update its voice counterpart after a
				// start_sample().
				voice_new.spl.interpol_flag = voice_cut.spl.interpol_flag;

				// Now, cut the old voice.
				voice_cut.cut ();
			}
		}

		// Cut failure is handled correctly but should be very rare.
		// It's probably caused by a bug (ghost voice not dying)
		assert (voice_index_cut != voice_index_new);
	}

	return (voice_index_new);
}



void	Player::kill_inactive_ghost_voices ()
{
	const int		nbr_tracks = GTK_nbr_tracks [Pattern_TYPE_SPL];
	for (int track_cnt = 0; track_cnt < nbr_tracks; ++track_cnt)
	{
		TrackInfo &		track_info =
			_track_info [_track_info_list [Pattern_TYPE_SPL] [track_cnt]];

		for (int ghost_cnt = 1; ghost_cnt < track_info.mix.spl.nbr_voices; )
		{
			const int		ghost_index =
				track_info.mix.spl.voice_index_arr [ghost_cnt];
			Voice &			voice = _voice_arr [ghost_index];
			if (voice.is_active ())
			{
				++ ghost_cnt;
			}
			else
			{
				deallocate_voice (ghost_index);
				-- track_info.mix.spl.nbr_voices;
				track_info.mix.spl.voice_index_arr [ghost_cnt] =
					track_info.mix.spl.voice_index_arr [track_info.mix.spl.nbr_voices];
			}
		}
	}
}



void	Player::init_filter (TrackInfo &track_info)
{
	assert (&track_info != 0);

	Voice &			voice = use_main_voice (track_info);

	const int		instr = track_info.score.instr;
	track_info.score.lpf.filter_flag = INST_get_filter_flag (instr);

	if (! track_info.score.lpf.filter_flag)
	{
		voice.filter.deactivate ();
	}

	else
	{
		track_info.score.lpf.freq_vol_flag = INST_get_filter_freq_vol_flag (instr);
		track_info.score.lpf.q_vol_flag = INST_get_filter_q_vol_flag (instr);
		const double	f1 = INST_get_filter_freq (instr, 0);
		const double	f2 = INST_get_filter_freq (instr, 1);
		const double	q1 = INST_get_filter_q (instr, 0);
		const double	q2 = INST_get_filter_q (instr, 1);
		track_info.score.lpf.freq [0] = f1;
		track_info.score.lpf.freq [1] = f2;
		track_info.score.lpf.q [0] = q1;
		track_info.score.lpf.q [1] = q2;

		voice.filter.activate (f1, f2, q1, q2);
	}
}



Voice &	Player::use_main_voice (TrackInfo &track_info)
{
	assert (&track_info != 0);

	const int		voice_index = track_info.mix.spl.voice_index_arr [0];

	return (_voice_arr [voice_index]);
}



void	Player::init_constants ()
{
	int				i;

	/* Table exp(x): volumes instruments [0...800] -> [0...800] */
	_instr_vol_table_exp [0] = 0;
	for (i = 1; i <= 0x800; i ++)
	{
		_instr_vol_table_exp [i] = static_cast <UWORD> (floor (pow (2.0, static_cast <double> (i) / 0x100 + 3) + 0.5));
	}

	/* Table ln(x): volumes instruments [0...800] -> [0...800] */
	_instr_vol_table_log [0] = 0;
	for (i = 1; i <= 0x800; i ++)
	{
		_instr_vol_table_log [i] = MAX (
			static_cast <UWORD> (floor ((log (static_cast <double> (i)) / LN2 - 3) * 0x100 + 0.5)),
			static_cast <UWORD> (0)
		);
	}

	/* Table exp(x): volumes pistes [0...100[ -> [8000...10000[ */
	for (i = 0; i < 0x100; i ++)
	{
		_track_vol_table_exp [i] = static_cast <UWORD> (floor (pow (2.0, static_cast <double> (i) / 0x100) * 0x8000 + 0.5));
	}

	/* Table ln(x): volumes pistes [0...100[ -> [0...100[ */
	for (i = 0; i < 0x100; i ++)
	{
		_track_vol_table_log [i] = static_cast <UWORD> (floor ((log (static_cast <double> (i + 0x100)) - log (static_cast <double> (0x100))) * 0x100 / LN2 + 0.5));
	}

	/* Table des periodes.
		De C#-2 a G+8 en 16emes de ton, +/- 3/2 tons de securite */
	for (i = 0; i < (3+(GTK_NBRNOTES_MAXI-1)+3)*8; i ++)
	{
		double		period = pow (2.0, static_cast <double> ((Sample_REF_C2+3)*8 - i) / (12*8)) * Player::REF_PERIOD;
		period = MIN (period, Player::MAX_PERIOD);
		period = MAX (period, Player::MIN_PERIOD);
		_note_period_table [i] = float (period);
	}

}



void	Player::copy_track_conf_without_state (PLAY_SOURCE_TRACK_CONF_BLOCK &dst, const PLAY_SOURCE_TRACK_CONF_BLOCK &src)
{
	assert (&dst != 0);
	assert (&src != 0);

	if (&dst != &src)
	{
		dst.track_type = src.track_type;
		dst.wet_flag   = src.wet_flag;
		dst.track_nbr  = src.track_nbr;
		dst.inpvol     = src.inpvol;
		dst.inppan     = src.inppan;
		dst.cr.copy_without_state (src.cr);
	}
}



float	Player::_note_period_table [(3+(GTK_NBRNOTES_MAXI-1)+3)*8];
float *	Player::_note_period_table_ref = Player::_note_period_table + 3*8;

UWORD	Player::_instr_vol_table_exp [0x801];
UWORD	Player::_instr_vol_table_log [0x801];
UWORD	Player::_track_vol_table_exp [0x100];
UWORD	Player::_track_vol_table_log [0x100];



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
