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

        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.

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



/*
Penser a:
	- Finir la boucle principale
	- Faire les tracks on/off
*/

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

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

#include "base.h"
#include "base_ct.h"
#include	"config.h"
#include	"ConfigFile.h"
#include	"ConfigKey.h"
#include	"ConfigKeyValue.h"
#include "d2d.h"
#include "env.h"
#include "errors.h"
#include "file.h"
#include "fnames.h"
#include "fx_p.h"
#include "gtracker.h"
#include "inst.h"
#include "intrface.h"
#include "keyboard.h"
#include	"log.h"
#include "memory.h"
#include "mixer.h"
#include "mods_ct.h"
#include "modstruc.h"
#include "Mouse.h"
#include "mpannel.h"
#include "os.h"
#include	"PatEvent.h"
#include	"PatEventField.h"
#include "patt.h"
#include	"Pattern.h"
#include "Player.h"
#include "resource.h"
#include "samp.h"
#include "song.h"
#include "splstruc.h"
#include	"Thread.h"
#include	"tracks.h"
#include	"UndoList.h"
#include	"WaveForm.h"



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



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



/*\\\ PROTOTYPES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

signed int	GTK_initialisations (int argc, char *argv []);
signed int	GTK_restore (void);

void	GTK_parse_command_line (int argc, char *argv []);

signed int	GTK_main_loop (void);



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

/* Sert a proteger toutes les donnees de la partition quand on s'en sert */
std::mutex	GTK_mutex;

/* Indique si le module est susceptible d'avoir ete modifie. Ce flag vient en
	complement aux informations apportees par le gestionnaire d'Undo/Redo,
	c'est a dire qu'il ne prend pas en compte toutes les actions enregistrees
	dans des blocs d'Undo. */
bool	GTK_modified_flag = false;

/* Objet des blocs pour les outils de pattern */
PatternTools	*GTK_pattern_block_ptr = NULL;

/* Liste des Undo/Redo */
UndoList		*GTK_undo_list_ptr = NULL;

/* Nombre de pistes de production sonore. Il doit
   y avoir au moins une piste de samples. */
int	GTK_nbr_tracks [GTK_NBR_MIX_TRACK_TYPE] =
{
	GTK_INIT_NBR_SPL_TRACKS,
	0,
	GTK_INIT_NBR_FX_TRACKS,
	GTK_INIT_NBR_MID_TRACKS,
	0
};

int	GTK_instr_nbr = 1;			/* Numero de l'instrument courant */
int	GTK_sample_nbr = 1;			/* Numero du sample courant */
int	GTK_mix_nbr = 1;				/* Numero du preset de mixage courant */
int	GTK_fx_preset_nbr = 1;		/* Numero du preset d'effet courant */
bool	GTK_fx_real_time_mode_flag = true;	/* Mode temps reel pour les effets. */
bool	GTK_spl_instr_interdep_flag = false;	/* Indique que les numeros de samples et d'instruments sont solidaires */

/* Taille en samples pour un buffer de D2D */
LWORD	GTK_default_d2d_buffer_len = 16384;

/* Divers */
int	GTK_octave = 3;				/* Octave du bas du clavier. C4 = reference */
signed int	GTK_line_step = 1;	/* Lignes a avancer lors de l'entree d'une donnee dans le pattern */

/* Sert a mesurer des temps pour le debuggage (tests de performances) */
QWORD	GTK_test_time [10];

/* Est mis a true quand l'utilisateur decide de sortir. On lui demande alors
	ce qu'il veut vraiment faire. */
bool	GTK_ask_for_exit_flag = false;

/* Mis a true pour ordonner a l'application doit quitter immediatement. C'est
   la consequence logique de la mise a true de GTK_ask_for_exit_flag. */
bool	GTK_exit_now_flag = false;

/* Demande de changement de resolution. Ce changement n'est execute que dans
   la boucle principale pour eviter des conflits entre la nouvelle resolution
   et un affichage parallele avec les donnees de l'ancienne resolution. */
bool	GTK_ask_for_resolution_change_flag = false;
int	GTK_new_res_width = 800;
int	GTK_new_res_height = 600;

/* Nom du fichier qui vient d'etre traine dans la fenetre */
char	GTK_drag_and_drop_filename_0 [FNAM_PATHNAME_MAXLEN+1];
bool	GTK_ask_for_loading_module_flag = false;	// true si on vient de faire du drag & drop avec un module.
char	GTK_skin_pathname_0 [FNAM_PATHNAME_MAXLEN+1];
bool	GTK_ask_for_loading_skin_flag = false;	// true si on vient de faire du drag & drop avec une skin.



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

/*** Ce sont des variables temporaires qui ne  ***/
/*** devraient peut-etre pas exister au final. ***/
int	GTK___edit_status = 0;



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



signed int GTK_main (int argc, char *argv [])
{
	if (GTK_initialisations (argc, argv) == 0)
	{
		GTK_main_loop ();
	}

	GTK_restore ();

	return (0);
}



/*==========================================================================*/
/*      Nom: GTK_initialisations                                            */
/*      Description: Initialisations du Graoumf Tracker. Sortie directe en  */
/*                   cas d'erreur fatale.                                   */
/*      Parametres en entree:                                               */
/*        - argc, argv: ligne de commandes standard.                        */
/*      Retour: 0 si aucune erreur.                                         */
/*==========================================================================*/

signed int	GTK_initialisations (int argc, char *argv [])
{
	time_t	start_time;
	char		welcome_mes_0 [99+1];

	/* Recupere le chemin du programme */
	if (FILE_get_program_path ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't get program path.\n");
		return (-1);
	}

	/* Assertions diverses */
	assert (GTK_NBRTRACKS_MAXI <= 99);

	LOG_printf ("\n\n\n*** Graoumf Tracker ***\n");

	LOG_printf ("%s r%d, compiled %s at %s.\n",
	            "Version",
	            GTK_VERSION_NUMBER, __DATE__, __TIME__);
	time (&start_time);
	LOG_printf ("Session of %s\n", ctime (&start_time));

	/* Initialisation du gestionnaire de debuggage */
	if (ERR_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't error handler and debugging system.\n");
		return (-1);
	}

	// Loads the persistent MRU file list
	MODS_load_mru_file_list ();

	/* Decrypte la ligne de commandes */
	GTK_parse_command_line (argc, argv);

	/* Init du systeme de fichiers */
	if (FILE_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init file system.\n");
		return (-1);
	}

	/* Detection de la machine, initialisation du clavier */
	if (OS_first_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init system.\n");
		return (-1);
	}

	/* Initialisation des formes d'onde*/
	if (WaveForm::init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init waveforms.\n");
		return (-1);
	}

	/* Chargement des preferences */
	FILE_load_init_prefs ();

	/* Initialisation des presets d'effets */
	if (FXP_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init effect presets.\n");
		return (-1);
	}

	/* Initialisation des variables de l'interface */
	if (INTR_init_interface ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init interface.\n");
		return (-1);
	}

	/* Intialisation des routines de chargement/sauvegarde de samples */
	if (SPLS_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init sample file routines.\n");
		return (-1);
	}
	
	/* Initialisation du systeme sonore */
	if (OS_init_sound_system ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init sound system.\n");
		return (-1);
	}

	Player &			player = Player::use_instance ();

	/* Premiere initialisation du player */
	if (player.first_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init player.\n");
		return (-1);
	}

	/* Initialisation du cache du Direct-2-Disk */
	if (D2D_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init D2D engine.\n");
		return (-1);
	}

	/* Initialisation du mixer */
	if (MIX_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init mixer.\n");
		return (-1);
	}

	/* Initialisation du clavier */
	if (KEYB_init_keyboard ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init keyboard.\n");
		return (-1);
	}

	/* Initialisation des presets de mixage */
	if (MIXP_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init mix presets.\n");
		return (-1);
	}

	/* Initialisation des patterns */
	if (PAT_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init patterns.\n");
		return (-1);
	}

	/* Initialisation des instruments */
	if (INST_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init instruments.\n");
		return (-1);
	}

	/* Initialisation des envelopes */
	if (ENV_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init envelopes.\n");
		return (-1);
	}

	/* Initialisation des samples */
	if (SAMP_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init samples.\n");
		return (-1);
	}

	/* Initialisation de la song */
	if (SONG_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't init song.\n");
		return (-1);
	}

	/* Mesure de la frequence d'horloge du processeur */
	OS_processor_frequency_measurement ();

	/* Mise en route du player */
	if (player.second_init ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't start player.\n");
		return (-1);
	}

	/* Initialisation des outils de blocs des patterns */
	GTK_pattern_block_ptr = new PatternTools;
	if (GTK_pattern_block_ptr->check_ok ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't create pattern toolbox.\n");
		return (-1);
	}

	/* Initialisation du gestionnaire d'Undo/Redo */
	GTK_undo_list_ptr = new UndoList ();
	if (GTK_undo_list_ptr->check_ok ())
	{
		LOG_printf ("GTK_initialisations: Error: couldn't create object for Undo/Redo functions.\n");
		return (-1);
	}

	/* Panneau principal */
	INTR_redraw_all ();

	/* Autoloading */
	if (FILE_autoloading ())
	{
		/* Erreur fatale durant l'auto-chargement */
		LOG_printf ("GTK_initialisations: Error: couldn't load workspace.\n");
		return (-1);
	}

	sprintf (
		welcome_mes_0,
		"GRAOUMF TRACKER r%d (c) 1994-%d Laurent de Soras",
		GTK_VERSION_NUMBER,
		GTK_LAST_MOD_YEAR
	);
	MPAN_display_message (welcome_mes_0);
	INTR_graph_ptr->mouse_arrow ();

	#if 0
		strftime (beta_message_0, 4095,
		          "This beta version of GRAOUMF TRACKER is time-limited.\n"
					 "You can save your work until %B %d %Y. After\n"
					 "this date, please get a more recent beta version or\n"
					 "purchase the final product if it is available.\n"
					 "\n"
					 "Information and last beta versions can be found at:\n"
					 "http://www.multimania.com/ldesoras/gtracker2/",
					 &end_date);
		INTR_graph_ptr->get_mouse (&INTR_mouse);
		INTR_dialog_box ("BETA VERSION", beta_message_0, "OK", 0, 0);
	#endif

	/* On commence avec ca */
	GTK_modified_flag = false;

	return (0);
}



/*==========================================================================*/
/*      Nom: GTK_restore                                                    */
/*      Description: Restoration du contexte d'arrivee.                     */
/*      Retour: 0 si aucune erreur.                                         */
/*==========================================================================*/

signed int	GTK_restore (void)
{
	INTR_do_waiting_op (true);

	if (GTK_undo_list_ptr != NULL)
	{
		delete GTK_undo_list_ptr;
		GTK_undo_list_ptr = NULL;
	}

	if (GTK_pattern_block_ptr != NULL)
	{
		delete GTK_pattern_block_ptr;
		GTK_pattern_block_ptr = NULL;
	}

	Player &			player = Player::use_instance ();

	player.second_restore ();
	SONG_restore ();
	SAMP_restore ();
	ENV_restore ();
	INST_restore ();
	PAT_restore ();
	MIXP_restore ();
	KEYB_restore_keyboard ();
	MIX_restore ();
	D2D_restore ();
	player.first_restore ();
	OS_restore_sound_system ();
	SPLS_restore ();
	INTR_restore_interface ();
	FXP_restore ();
	WaveForm::restore ();
	OS_first_restore ();
	FILE_restore ();
	ERR_restore ();

	/* Pour le debuggage des mallocs */
	#if ! defined (NDEBUG)
		MEM_memstat ();
	#endif

	return (0);
}



/*==========================================================================*/
/*      Nom: GTK_parse_command_line                                         */
/*      Description: Recupere les informations de la ligne de commandes.    */
/*      Parametres en entree:                                               */
/*        - argc, argv: ligne de commandes standard.                        */
/*==========================================================================*/

void	GTK_parse_command_line (int argc, char *argv [])
{
	if (argc > 1)
	{

		/*** A faire ***/

		/* Dernier parametre: module a charger */
		if (argv [argc - 1] [0] != '-')
		{
			strncpy (GTK_drag_and_drop_filename_0, argv [argc - 1], FNAM_PATHNAME_MAXLEN);
			GTK_drag_and_drop_filename_0 [FNAM_PATHNAME_MAXLEN] = '\0';
			GTK_ask_for_loading_module_flag = true;
		}
	}
}



/*==========================================================================*/
/*      Nom: GTK_main_loop                                                  */
/*      Description: Boucle principale.                                     */
/*      Retour: 0 si aucune erreur.                                         */
/*==========================================================================*/

signed int	GTK_main_loop (void)
{
	int		button;
	bool		click_flag;
	int		old_main_screen;

	while (! GTK_exit_now_flag)
	{

		/* Detection des evenements souris */
		INTR_graph_ptr->show_mouse ();
		INTR_graph_ptr->get_mouse (&INTR_mouse);

/*______________________________________________
 *
 * Gestion de l'ecran principal
 *______________________________________________
 */

		old_main_screen = INTR_main_screen;

		/* Gestion */
		switch (INTR_main_screen)
		{

		/* Ecran des patterns */
		case	INTR_MAIN_SCREEN_PATTERN_EDITOR:

			click_flag = MPAN_manage_all ();
			KEYB_mp_keyboard ();

			/* Mises a jour de l'affichage en mode Play */
			{
				const Player &	player = Player::use_instance ();
				if (player.get_play_mode () != Player::MODE_STOP)
				{
					MPAN_refresh_dynamic_display ();
				}
			}
			break;

		/* Editeur de samples */
		case	INTR_MAIN_SCREEN_SAMPLE_EDITOR:
			INTR_sample_editor_interface_ptr->manage (-1, -1);
			break;

		/* Editeur de partition */
		case	INTR_MAIN_SCREEN_SCORE_EDITOR:

			/*** A faire ***/

			break;

		/* Editeur de batterie */
		case	INTR_MAIN_SCREEN_DRUM_EDITOR:

			/*** A faire ***/

			break;

		}

		/* Affichage du nouvel ecran si on a change de page */
		if (INTR_main_screen != old_main_screen)
		{
			INTR_do_waiting_op (true);
			INTR_redraw_all ();
		}

/*______________________________________________
 *
 * Gestion du reste des evenements
 *______________________________________________
 */

		/* Actions retardees */
		INTR_do_waiting_op (false);

		/* Demande de changement de resolution */
		if (GTK_ask_for_resolution_change_flag)
		{
			INTR_graph_ptr->change_resolution (GTK_new_res_width, GTK_new_res_height);
			INTR_redraw_all ();
			GTK_ask_for_resolution_change_flag = false;
		}

		/* Sortie demandee par l'utilisateur */
		if (GTK_ask_for_exit_flag)
		{
			GTK_ask_for_exit_flag = false;
			button = INTR_dialog_box ("QUIT", "Do you really want to\nquit GRAOUMF TRACKER \?", "Yes, sir\nBof...\nNo, No !", 0, 2);
			if (   button == 0
				 || (button == 1 && rand () > RAND_MAX/2))
			{
				GTK_exit_now_flag = true;
			}
		}

		/* Fichier en drag & drop */
		if (GTK_ask_for_loading_module_flag)
		{
			MODS_load_module (false, GTK_drag_and_drop_filename_0);
			GTK_ask_for_loading_module_flag = false;
			INTR_redraw_all ();
			INTR_play_song (false);
		}

		if (GTK_ask_for_loading_skin_flag)
		{
			if (FILE_file_exist (GTK_skin_pathname_0))
			{
				if (RSC_set_skin (GTK_skin_pathname_0))
				{
					INTR_dialog_box ("LOAD SKIN", "Error: couldn't load new skin.", "Cancel", 0, 0);
				}
				INTR_redraw_all ();
			}
			else
			{
				INTR_dialog_box ("LOAD SKIN",
				                 (String ("Error: file doesn't exist.\n") + GTK_skin_pathname_0).c_str (),
				                 "Cancel", 0, 0);
			}
			GTK_ask_for_loading_skin_flag = false;
		}

		/* Rend la main 20 ms pour economiser des resources, le temps que
		   le pattern courant change ou que l'utilisateur fasse une action. */
		Thread::sleep (20);
	}

	/* Comme on sort de la boucle, on force les operations en attente */
	INTR_do_waiting_op (true);

	return (0);
}



/****************************************************************************/
/*                                                                          */
/*      MODIFICATION DES VARIABLES                                          */
/*                                                                          */
/****************************************************************************/



int	GTK_get_song_position (void)
{
	const Player &	player = Player::use_instance ();
	return (player.get_song_position ());
}



void	GTK_set_song_position (int songpos)
{
	int		last_songpos;

	last_songpos = SONG_get_song_length () - 1;
	songpos = MIN (songpos, last_songpos);
	songpos = MAX (songpos, 0);

	Player &			player = Player::use_instance ();
	player.set_song_position (songpos);

	INTR_pattern_editor_track_ptr->check_cursor ();
}



int	GTK_get_current_pattern_number (void)
{
	return (SONG_get_pattern_number (GTK_get_song_position ()));
}



void	GTK_set_current_pattern_number (int pattern)
{
	SONG_set_pattern_number (GTK_get_song_position (), pattern);

	INTR_pattern_editor_track_ptr->check_cursor ();
}



int	GTK_get_line_position (void)
{
	const Player &	player = Player::use_instance ();

	return (player.get_line_position ());
}



void	GTK_set_line_position (int linepos)
{
	int		last_line;

	last_line = PAT_get_pattern_height (GTK_get_current_pattern_number ()) - 1;
	linepos = MIN (linepos, last_line);
	linepos = MAX (linepos, 0);

	Player &			player = Player::use_instance ();
	player.set_line_position (linepos);

	INTR_pattern_editor_track_ptr->check_cursor ();
}



void	GTK_set_cur_instr (int instr)
{
	instr = MIN (instr, INST_NBRINSTR_MAXI);
	instr = MAX (instr, 1);
	GTK_instr_nbr = instr;

	/* Si les samples et les instruments sont interdependants,
		il faut corriger le numero du sample. */
	if (GTK_spl_instr_interdep_flag)
	{
		GTK_sample_nbr = INST_get_instr_sample (GTK_instr_nbr, Sample_REF_C2);

		switch (INTR_main_screen)
		{
		case	INTR_MAIN_SCREEN_PATTERN_EDITOR:
			MPAN_display_var_icons (true, true);
			if (INTR_pattern_editor_menu_ptr->get_submenu () == PatEdMenus_SPL)
			{
				INTR_pattern_editor_menu_ptr->refresh ();
			}
			break;

		case	INTR_MAIN_SCREEN_SAMPLE_EDITOR:
			INTR_sample_editor_interface_ptr->refresh ();
			break;
		}
	}
}



void	GTK_set_cur_sample (int sample)
{
	sample = MIN (sample, SAMP_NBRSAMPLES_MAXI);
	sample = MAX (sample, 1);
	GTK_sample_nbr = sample;

	/* Si les samples et les instruments sont interdependants,
		il faut corriger le numero de l'instrument. */
	if (GTK_spl_instr_interdep_flag)
	{
		/* Recherche l'instrument qui associe le sample courant a la note C-2 */
		for (int instr = 1; instr <= INST_NBRINSTR_MAXI; ++instr)
		{
			if (INST_get_instr_sample (instr, Sample_REF_C2) == GTK_sample_nbr)
			{
				GTK_instr_nbr = instr;
				break;
			}
		}

		switch (INTR_main_screen)
		{
		case	INTR_MAIN_SCREEN_PATTERN_EDITOR:
			MPAN_display_divers ();
			if (   INTR_pattern_editor_menu_ptr->get_submenu () == PatEdMenus_INSTR
			    || INTR_pattern_editor_menu_ptr->get_submenu () == PatEdMenus_ENV)
			{
				INTR_pattern_editor_menu_ptr->refresh ();
			}

		case	INTR_MAIN_SCREEN_SAMPLE_EDITOR:
			break;
		}
	}
}



/*** A faire ***/
int	GTK_get_edit_status (void)
{
	return (GTK___edit_status);
}



/*** A faire ***/
void	GTK_set_edit_status (int status)
{
	GTK___edit_status = status;

	GTK_set_correct_cursor_color ();
}



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



/*==========================================================================*/
/*      Nom: GTK_mod_is_modified                                            */
/*      Description: Indique si le module a ete modifie.                    */
/*      Retour: true s'il l'a ete, false sinon.                             */
/*==========================================================================*/

bool	GTK_mod_is_modified (void)
{
	return (   GTK_modified_flag
	        || GTK_undo_list_ptr->get_undo_count () > 0);
}



/*==========================================================================*/
/*      Nom: GTK_read_module_editing_conf                                   */
/*      Description: Lit les informations concernant l'edition du module    */
/*                   dans le fichier de configuration.                      */
/*      Parametres en entree:                                               */
/*        key_list_ptr: pointeur sur la liste des cles correspondant a la   */
/*                      cle "/module_editing" dans le fichier de            */
/*                      configuration.                                      */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	GTK_read_module_editing_conf (const std::list <ConfigKey> &key_list)
{
	int		int_val;

	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		/* 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 ("GTK_read_module_editing_conf: Warning: (in \"module_editing\") unexpected unassigned value.\n");
		}

		/* Default pattern height */
		else if (BASE_compare_string (key_name.c_str (), "default_pattern_height") == 0)
		{
			if (CFG_get_int (key_val, "module_editing/default_pattern_height", int_val))
			{
				int_val = MIN (int_val, GTK_NBRLINES_MAXI);
				PAT_new_pattern_nbr_lines = MAX (int_val, 1);
			}
		}

		/* Number of sample tracks */
		else if (BASE_compare_string (key_name.c_str (), "nbr_sample_tracks") == 0)
		{
			if (CFG_get_int (key_val, "module_editing/nbr_sample_tracks", int_val))
			{
				int_val = MIN (int_val, GTK_NBRTRACKS_MAXI);
				GTK_nbr_tracks [Pattern_TYPE_SPL] = MAX (int_val, 1);
			}
		}

		/* Number of effect tracks */
		else if (BASE_compare_string (key_name.c_str (), "nbr_effect_tracks") == 0)
		{
			if (CFG_get_int (key_val, "module_editing/nbr_effect_tracks", int_val))
			{
				int_val = MIN (int_val, GTK_NBRTRACKS_MAXI);
				GTK_nbr_tracks [Pattern_TYPE_FX] = MAX (int_val, 0);
			}
		}

		/* Number of MIDI tracks */
		else if (BASE_compare_string (key_name.c_str (), "nbr_midi_tracks") == 0)
		{
			if (CFG_get_int (key_val, "module_editing/nbr_midi_tracks", int_val))
			{
				int_val = MIN (int_val, GTK_NBRTRACKS_MAXI);
				GTK_nbr_tracks [Pattern_TYPE_MID] = MAX (int_val, 0);
			}
		}

		/* Multiple backups */
		else if (BASE_compare_string (key_name.c_str (), "nbr_mod_backups") == 0)
		{
			if (CFG_get_int (key_val, "module_editing/nbr_mod_backups", int_val))
			{
				int_val = MIN (int_val, 999);
				MODS_nbr_backups = MAX (int_val, 0);
			}
		}

		/* Instrument and sample interdependence */
		else if (BASE_compare_string (key_name.c_str (), "spl_instr_interdep") == 0)
		{
			CFG_get_bool (key_val, "module_editing/spl_instr_interdep", GTK_spl_instr_interdep_flag);
		}

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

	return (0);
}



/*==========================================================================*/
/*      Nom: GTK_note_section_to_string                                     */
/*      Description: Convertit une section de note en chaine de caracteres. */
/*      Parametres en entree:                                               */
/*        - data_ptr: pointeur sur les donnees de la section de la note.    */
/*        - type: type de la section de la note.                            */
/*      Parametres en sortie:                                               */
/*        - text_ptr: pointeur sur la chaine. Suffisamment d'espace doit    */
/*                    etre deja reserve. La chaine n'est pas terminee par 0 */
/*      Retour: Le nombre de caracteres que la conversion a necessite.      */
/*==========================================================================*/

int	GTK_note_section_to_string (const UBYTE *data_ptr, int type, char *text_ptr)
{
	int		rest;
	int		temp_val;
	unsigned int	temp_u;
	unsigned int	thousand;
	unsigned int	next_thousand;
	const char	*temp_ptr;

	switch (type)
	{
	case	TRK_CONTENT_NOTE:
		temp_ptr = INTR_note_names [*data_ptr];
		BASE_dpoke (text_ptr, BASE_dpeek (temp_ptr));
		text_ptr [2] = temp_ptr [2];
		return (3);
		break;

	case	TRK_CONTENT_INSTR:
		BASE_dpoke (text_ptr, ((WORD *)INTR_hexa_2_ascii_space) [*data_ptr]);
		return (2);
		break;

	case	TRK_CONTENT_COMMAND:
	case	TRK_CONTENT_EFFECT:
		temp_val = *data_ptr;
		if (temp_val != 0)
		{
			BASE_dpoke (text_ptr, ((WORD *)INTR_hexa_2_ascii_point) [temp_val]);
			BASE_dpoke (text_ptr + 2, ((WORD *)INTR_hexa_2_ascii) [data_ptr [1]]);
		}
		else
		{
			BASE_dpoke (text_ptr, 0x0909);
			BASE_dpoke (text_ptr + 2, ((WORD *)INTR_hexa_2_ascii_point) [data_ptr [1]]);
		}
		return (4);
		break;

	case	TRK_CONTENT_VOLUME:
		if (*data_ptr < 0x10)
		{
			BASE_dpoke (text_ptr, 0x2020);
		}
		else if (*data_ptr < 0x60)
		{
			BASE_dpoke (text_ptr, ((WORD *)INTR_hexa_2_ascii_space) [*data_ptr - 0x10]);
			if (*data_ptr == 0x10)
			{
				text_ptr [1] = '0';
			}
		}
		else
		{
			*text_ptr = TRK_vol_col_fx [*data_ptr >> 4];
			text_ptr [1] = BASE_digit_to_char (*data_ptr & 0x0F);
		}
		return (2);
		break;

	case	TRK_CONTENT_EFFECT_6H:
		BASE_dpoke (text_ptr, ((WORD *)INTR_hexa_2_ascii) [*data_ptr]);
		text_ptr [2] = ' ';
		BASE_dpoke (text_ptr + 3, ((WORD *)INTR_hexa_2_ascii) [data_ptr [1]]);
		temp_val = data_ptr [2];
		text_ptr [5] = BASE_digit_to_char (temp_val >> 4);
		text_ptr [6] = ' ';
		text_ptr [7] = BASE_digit_to_char (temp_val & 0xF);
		BASE_dpoke (text_ptr + 8, ((WORD *)INTR_hexa_2_ascii) [data_ptr [3]]);
		return (10);
		break;

	case	TRK_CONTENT_4H:
		BASE_dpoke (text_ptr, ((WORD *)INTR_hexa_2_ascii) [*data_ptr]);
		BASE_dpoke (text_ptr + 2, ((WORD *)INTR_hexa_2_ascii) [data_ptr [1]]);
		return (4);
		break;

	case	TRK_CONTENT_2D:
		temp_val = *data_ptr;
		temp_u = ((unsigned int)temp_val << 8) + data_ptr [1];
		thousand = INTR_dec_2_ascii_1000_int [temp_val];
		next_thousand = INTR_dec_2_ascii_1000_int [temp_val + 1];
		if (next_thousand > thousand)
		{
			if (temp_u >= next_thousand)
			{
				temp_val ++;
				thousand = next_thousand;
			}
		}
		rest = temp_u - thousand;

		BASE_dpoke (text_ptr, BASE_dpeek (INTR_dec_2_ascii_1000_char_s2 [temp_val]));
		temp_ptr = INTR_dec_2_ascii_units_char [rest];
		text_ptr [2] = *temp_ptr;
		text_ptr [3] = '.';
		BASE_dpoke (text_ptr + 4, BASE_dpeek (temp_ptr + 1));
		return (6);
		break;

	case	TRK_CONTENT_3D:
		temp_val = *data_ptr;
		temp_u = ((unsigned int)temp_val << 8) + data_ptr [1];
		thousand = INTR_dec_2_ascii_1000_int [temp_val];
		next_thousand = INTR_dec_2_ascii_1000_int [temp_val + 1];
		if (next_thousand > thousand)
		{
			if (temp_u >= next_thousand)
			{
				temp_val ++;
				thousand = next_thousand;
			}
		}
		rest = temp_u - thousand;

		BASE_dpoke (text_ptr, BASE_dpeek (INTR_dec_2_ascii_1000_char_s1 [temp_val]));
		temp_ptr = INTR_dec_2_ascii_units_char [rest];
		text_ptr [2] = '.';
		text_ptr [3] = *temp_ptr;
		BASE_dpoke (text_ptr + 4, BASE_dpeek (temp_ptr + 1));
		return (6);
		break;

	case	TRK_CONTENT_INT:
		temp_val = *data_ptr;
		temp_u = ((unsigned int)temp_val << 8) + data_ptr [1];
		thousand = INTR_dec_2_ascii_1000_int [temp_val];
		next_thousand = INTR_dec_2_ascii_1000_int [temp_val + 1];
		if (next_thousand > thousand)
		{
			if (temp_u >= next_thousand)
			{
				temp_val ++;
				thousand = next_thousand;
			}
		}
		rest = temp_u - thousand;

		BASE_dpoke (text_ptr, BASE_dpeek (INTR_dec_2_ascii_1000_char_s2 [temp_val]));
		temp_ptr = INTR_dec_2_ascii_units_char [rest];
		BASE_dpoke (text_ptr + 2, BASE_dpeek (temp_ptr));
		text_ptr [4] = temp_ptr [2];
		return (5);
		break;

	case	TRK_CONTENT_2H:
		BASE_dpoke (text_ptr, ((WORD *)INTR_hexa_2_ascii_point) [*data_ptr]);
		return (2);
		break;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: GTK_note_to_string                                             */
/*      Description: Convertit une note en une chaine de caracteres.        */
/*      Parametres en entree:                                               */
/*        - note_ptr: pointeur sur le debut de la note.                     */
/*        - track_type: type de la piste, non etendu.                       */
/*        - display_type: type d'affichage. Correspond au nombre de champs  */
/*                        affiches.                                         */
/*      Parametres en sortie:                                               */
/*        - text_ptr: pointeur sur la chaine. Suffisamment d'espace doit    */
/*                    etre deja reserve. La chaine n'est pas terminee par 0 */
/*      Retour: Le nombre de caracteres occupees par la note totale.        */
/*==========================================================================*/

int	GTK_note_to_string (const void *note_ptr, char *text_ptr, int track_type, int display_type)
{
	int		col_cnt;
	int		char_cnt;
	int		last_col;
	int		field;
	int		content;
	int		nbr_spaces;

	track_type = GTK_get_ext_track_type (track_type, note_ptr);
	last_col = TRK_cursor_last_pos [track_type] [display_type];
	char_cnt = 0;
	col_cnt = 0;
	field = 0;
	do
	{
		content = TRK_note_content [track_type] [field].content;
		char_cnt += GTK_note_section_to_string ((const UBYTE *) note_ptr, content, text_ptr + char_cnt);
		nbr_spaces = TRK_note_content [track_type] [field].space;
		if (nbr_spaces > 0)
		{
			do
			{
				text_ptr [char_cnt] = ' ';
				char_cnt ++;
				nbr_spaces --;
			}
			while (nbr_spaces > 0);
		}
		note_ptr = (const void *) ((const UBYTE *)note_ptr + TRK_note_element [content].nbr_bytes);
		col_cnt += TRK_note_element [content].nbr_col;
		field ++;
	}
	while (col_cnt <= last_col);

	return (char_cnt);
}



/*==========================================================================*/
/*      Nom: GTK_get_ext_track_type                                         */
/*      Description: Donne le type etendu d'une piste selon la note. Sert   */
/*                   en fait pour l'affichage.                              */
/*      Parametres en entree:                                               */
/*        - track_type: type normal de la piste.                            */
/*        - note_ptr: pointeur sur la note.                                 */
/*      Retour: Type entendu.                                               */
/*==========================================================================*/

int	GTK_get_ext_track_type (int track_type, const void *note_ptr)
{
	if (track_type == Pattern_TYPE_FX)
	{
		return (GTK_get_fx_ext_track_type (note_ptr));
	}

	if (track_type == Pattern_TYPE_MID)
	{
		return (GTK_get_mid_ext_track_type (note_ptr));
	}

	return (track_type);
}



int	GTK_get_fx_ext_track_type (void const *note_ptr)
{
	unsigned int	command;
	int		fx;
	int		sub_cmd;

	command = (unsigned int) (UWORD) BASE_dpeek_moto (note_ptr);

	if (command < ((long)FxPreset_NBR_TYPES << 8))
	{
		fx = (command >> 8) & 0xFF;
		sub_cmd = command & 0xFF;

		return (Player::_fx_command_format_ptr [fx] [sub_cmd]);
	}

	if (command >= 0xFF00U)
	{
		return (Pattern_TYPE_FX);
	}

	if (command >= 0xC000U)
	{
		return (TRK_EXT_TRACK_TYPE_FX_4H);
	}

	return (TRK_EXT_TRACK_TYPE_FX_2D);
}



int	GTK_get_mid_ext_track_type (const void *note_ptr)
{

	/*** A finir ***/

	return (Pattern_TYPE_MID);
}



/*==========================================================================*/
/*      Nom: GTK_next_line                                                  */
/*      Description: Saute a la ligne suivante en tenant compte du step.    */
/*==========================================================================*/

void	GTK_next_line (void)
{
	/*** A finir ***/

	int		ligne;
	int		nbr_lines;
	int		track_type;

	track_type = TRK_preset_data [TRK_preset_nbr].track_type;

	switch (GTK_line_step)
	{
		/* Prochaine ligne occupee */
		case	INTR_LINE_STEP_NL:
		{
			switch (track_type)
			{
			case	Pattern_TYPE_SPL:
				GTK_cherche_next_line_spl ((char) 0xFF, 0xFFFFFFFFL, true);
				break;
			case	Pattern_TYPE_AIN:
				break;
			case	Pattern_TYPE_FX:
				break;
			case	Pattern_TYPE_MID:
				break;
			}
			break;
		}

		/* Prochaine note */
		case	INTR_LINE_STEP_NN:
		{
			switch (track_type)
			{
			case	Pattern_TYPE_SPL:
				GTK_cherche_next_line_spl ((char) 0xFF, 0x00000000, true);
				break;
			case	Pattern_TYPE_AIN:
				break;
			case	Pattern_TYPE_FX:
				break;
			case	Pattern_TYPE_MID:
				break;
			}
			break;
		}

		/* Prochain effet */
		case	INTR_LINE_STEP_NF:
		{
			switch (track_type)
			{
			case	Pattern_TYPE_SPL:
				GTK_cherche_next_line_spl ((char) 0x00, 0x00FFFF00L, true);
				break;
			case	Pattern_TYPE_AIN:
				break;
			case	Pattern_TYPE_FX:
				break;
			case	Pattern_TYPE_MID:
				break;
			}
			break;
		}

		/* Prochain volume */
		case	INTR_LINE_STEP_NV:
		{
			switch (track_type)
			{
			case	Pattern_TYPE_SPL:
				GTK_cherche_next_line_spl ((char) 0x00, 0x000000FF, true);
				break;
			case	Pattern_TYPE_AIN:
				break;
			case	Pattern_TYPE_FX:
				break;
			case	Pattern_TYPE_MID:
				break;
			}
			break;
		}

		/* Prochain effet inoccupe */
		case	INTR_LINE_STEP_NEF:
		{
			switch (track_type)
			{
			case	Pattern_TYPE_SPL:
				GTK_cherche_next_line_spl ((char) 0x00, 0x00FFFF00L, false);
				break;
			case	Pattern_TYPE_AIN:
				break;
			case	Pattern_TYPE_FX:
				break;
			case	Pattern_TYPE_MID:
				break;
			}
			break;
		}

		/* Saut de ligne normal */
		default:
		{
			ligne = GTK_get_line_position () + GTK_line_step;
			nbr_lines = PAT_get_current_pattern_height ();
			while	(ligne >= nbr_lines)
			{
				ligne -= nbr_lines;
			}
			while	(ligne < 0)
			{
				ligne += nbr_lines;
			}
			GTK_set_line_position (ligne);
			break;
		}
	}
	INTR_pattern_editor_track_ptr->refresh_dynamic (true);
}



/*==========================================================================*/
/*      Nom: GTK_cherche_next_line_???                                      */
/*      Description: Cherche la plus proche ligne qui correspond a un       */
/*                   certain masque et y saute.                             */
/*      Parametres en entree:                                               */
/*        - mask1, mask2: les masques de note.                              */
/*        - full_flag: indique si on doit rechercher du vide ou du plein a  */
/*                     travers le masque.                                   */
/*==========================================================================*/

void	GTK_cherche_next_line_spl (BYTE mask1, LWORD mask2, bool full_flag)
{
	int		start;
	int		ligne;
	int		pattern;
	int		track;
	int		nbr_lines;
	BYTE		a;
	LWORD		b;
	bool		found_flag;
	const MODS_GT2_SPL_NOTE	*ptr_note;

	found_flag = false;
	start = GTK_get_line_position ();
	ligne = start;
	pattern = GTK_get_current_pattern_number ();
	track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
	nbr_lines = PAT_get_current_pattern_height ();
	do
	{
		ligne ++;
		if (ligne >= nbr_lines)
		{
			ligne = 0;
		}
		ptr_note = (MODS_GT2_SPL_NOTE *)(PAT_get_spl_note_adr_pat (pattern, ligne, track));
		a = ptr_note->note & mask1;
		b = BASE_lpeek_moto (&ptr_note->instr) & mask2;
		if (full_flag)
		{
			if (a != 0 || b != 0)
			{
				found_flag = true;
			}
		}
		else if (a == 0 && b == 0)
		{
			found_flag = true;
		}
	}
	while (!found_flag && ligne != start);
	GTK_set_line_position (ligne);
}



/*==========================================================================*/
/*      Nom: GTK_stop_all                                                   */
/*      Description: Stoppe le replay et coupe tout son.                    */
/*==========================================================================*/

void	GTK_stop_all (void)
{
	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 ++)
		{
			PAT_clear_note (track_type, GTK_PATTERN_BIDON_A, 0, track);
		}
	}

	Player &			player = Player::use_instance ();
	player.set_play_mode (Player::MODE_STOP_ALL, -1, -1, false);
	
	GTK_set_correct_cursor_color ();
}



/*==========================================================================*/
/*      Nom: GTK_play_pattern_bidon                                         */
/*      Description: Joue les 2 patterns bidon et boucle sur le dernier.    */
/*==========================================================================*/

void	GTK_play_pattern_bidon (void)
{
	Player &			player = Player::use_instance ();
	player.set_play_mode (Player::MODE_STOP, -1, -1, false);
}



/*==========================================================================*/
/*      Nom: GTK_play_one_line                                              */
/*      Description: Joue une seule ligne de la partition dans les patterns */
/*                   bidons.                                                */
/*      Parametres en entree:                                               */
/*        - pattern: numero du pattern sur lequel on veut jouer la ligne.   */
/*        - line_pos: numero de la ligne a jouer.                           */
/*==========================================================================*/

void	GTK_play_one_line (int pattern, int line_pos)
{
	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 ++)
		{
			memcpy (PAT_get_note_adr_pat (track_type, GTK_PATTERN_BIDON_A, 0, track),
			        PAT_get_note_adr_pat (track_type, pattern, line_pos, track),
			        Pattern::NOTE_SIZE [track_type]);
		}
	}

	GTK_play_pattern_bidon ();
}



/*==========================================================================*/
/*      Nom: GTK_play_context                                               */
/*      Description: Joue toutes les notes qui etaient susceptibles d'etre  */
/*                   jouees avant d'arriver a la ligne demandee.            */
/*        - pattern: numero du pattern sur lequel on veut jouer la ligne.   */
/*        - line_pos: numero de la ligne dont on veut jouer le contexte.    */
/*==========================================================================*/

void	GTK_play_context (int pattern, int line_pos)
{
	int		track_type;
	int		track;
	int		line;
	int		index;
	PatEvent	event;
	PatEventField	*field_ptr;
	UBYTE		*data_ptr;
	UBYTE		field_data [16];

	/* Commence par vider le pattern bidon */
	for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
	{
		for (track = 0; track < GTK_nbr_tracks [track_type]; track ++)
		{
			PAT_clear_note (track_type, GTK_PATTERN_BIDON_A, 0, track);
		}
	}

	/* On joue toutes les notes qui se trouvent avant, en retirant
		les commandes de liaison. */
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; track ++)
	{
		for (line = line_pos - 1; line >= 0; line --)
		{
			data_ptr = (UBYTE *) PAT_get_note_adr_pat (Pattern_TYPE_SPL, pattern, line, track);
			event.set_data (Pattern_TYPE_SPL, data_ptr);
			index = 0;
			field_ptr = event.get_field_type (TRK_CONTENT_NOTE, index);

			/* Pas de note sur ce type de piste, on se casse */
			if (field_ptr == NULL)
			{
				break;
			}
			field_ptr->get_data (field_data);
			delete field_ptr;
			if (field_data [0] == 0)
			{
				continue;
			}

			index = 0;
			field_ptr = event.get_field_type (TRK_CONTENT_COMMAND, index);
			if (field_ptr != NULL)
			{
				field_ptr->get_data (field_data);
				if (   field_data [0] == 0x03
				    || field_data [0] == 0x05
				    || field_data [0] == 0x06
				    || field_data [0] == 0x18
				    || field_data [0] == 0x19
				    || field_data [0] == 0x1A
				    || field_data [0] == 0x1B
				    || field_data [0] == 0xAB)
				{
					memset (field_data, 0, TRK_note_element [TRK_CONTENT_COMMAND].nbr_bytes);
					field_ptr->set_data (field_data);
					event.set_field (index, field_ptr);
				}
				delete field_ptr;
			}
			data_ptr = (UBYTE *) PAT_get_note_adr_pat (Pattern_TYPE_SPL, GTK_PATTERN_BIDON_A, 0, track);
			event.get_data (Pattern_TYPE_SPL, data_ptr);
			break;
		}
	}

	/*** A finir ***/

	GTK_play_pattern_bidon ();
}



/*==========================================================================*/
/*      Nom: GTK_play_pattern                                               */
/*      Description: Joue le pattern courant en boucle.                     */
/*      Parametres en entree:                                               */
/*        - cont_flag: true si on doit jouer a partir de la ligne courante, */
/*                     false si le pattern doit etre joue depuis le debut.  */
/*==========================================================================*/

void	GTK_play_pattern (bool cont_flag)
{
	if (! cont_flag)
	{
		GTK_stop_all ();
	}

	Player &			player = Player::use_instance ();
	player.set_play_mode (Player::MODE_PATTERN, -1, -1, cont_flag);

	GTK_set_correct_cursor_color ();
}



/*==========================================================================*/
/*      Nom: GTK_play_song                                                  */
/*      Description: Joue le module.                                        */
/*      Parametres en entree:                                               */
/*        - cont_flag: true si on doit jouer a partir de la ligne courante, */
/*                     false si on doit jouer depuis le debut du pattern.   */
/*==========================================================================*/

void	GTK_play_song (bool cont_flag)
{
	if (! cont_flag)
	{
		GTK_stop_all ();
	}

	Player &			player = Player::use_instance ();
	player.set_play_mode (Player::MODE_SONG, -1, -1, cont_flag);

	GTK_set_correct_cursor_color ();
}



/*==========================================================================*/
/*      Nom: GTK_set_correct_cursor_color                                   */
/*      Description: Fixe la bonne couleur pour le curseur, selon le mode   */
/*                   de replay et d'edition.                                */
/*==========================================================================*/

void	GTK_set_correct_cursor_color (void)
{
	int		play_mode;

	const Player &	player = Player::use_instance ();
	play_mode = player.get_play_mode ();

	if (play_mode == Player::MODE_SONG)
	{
		INTR_graph_ptr->set_color (15, 0x0000FF00L);		/* Vert */
	}
	else if (play_mode == Player::MODE_PATTERN)
	{
		INTR_graph_ptr->set_color (15, 0x00FF8000L);		/* Orange */
	}
	else
	{
		INTR_graph_ptr->set_color (15, 0x00FFFFFFL);		/* Blanc */
	}

	if (GTK_get_edit_status ())
	{
		INTR_graph_ptr->set_color (15, 0x000000FFL);		/* Bleu */
	}
}



/*==========================================================================*/
/*      Nom: GTK_clear_song                                                 */
/*      Description: Efface la partition (song + patterns).                 */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	GTK_clear_song (void)
{
	if (SONG_clear_song ())
	{
		LOG_printf ("GTK_clear_song: Error: couldn't clear sequence.\n");
		return (-1);
	}

	if (PAT_clear_patterns ())
	{
		LOG_printf ("GTK_clear_song: Error: couldn't clear patterns.\n");
		return (-1);
	}

	SONG_set_small_comment ("");
	SONG_set_extra_comment ("");
	SONG_reset_date ();

	return (0);
}



/*==========================================================================*/
/*      Nom: GTK_clear_instruments                                          */
/*      Description: Efface les instruments + les samples.                  */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	GTK_clear_instruments (void)
{
	if (SAMP_clear_samples ())
	{
		LOG_printf ("GTK_clear_instruments: Error: couldn't clear samples.\n");
		return (-1);
	}

	if (INST_clear_instruments ())
	{
		LOG_printf ("GTK_clear_instruments: Error: couldn't clear instruments.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: GTK_clear_all                                                  */
/*      Description: Efface tout.                                           */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	GTK_clear_all (void)
{
	GTK_undo_list_ptr->reset ();

	if (GTK_clear_instruments ())
	{
		LOG_printf ("GTK_clear_all: Error: couldn't clear instruments and samples.\n");
		return (-1);
	}

	if (GTK_clear_song ())
	{
		LOG_printf ("GTK_clear_all: Error: couldn't clear sequence and patterns.\n");
		return (-1);
	}

	if (MIXP_clear_presets ())
	{
		LOG_printf ("GTK_clear_all: Error: couldn't clear mix presets.\n");
		return (-1);
	}

	if (FXP_clear_presets ())
	{
		LOG_printf ("GTK_clear_all: Error: couldn't clear effect presets.\n");
		return (-1);
	}

	PAT_set_nbr_tracks (Pattern_TYPE_SPL, GTK_INIT_NBR_SPL_TRACKS);
	PAT_set_nbr_tracks (Pattern_TYPE_FX, GTK_INIT_NBR_FX_TRACKS);
	PAT_set_nbr_tracks (Pattern_TYPE_MID, GTK_INIT_NBR_MID_TRACKS);
	
	Player &			player = Player::use_instance ();

	player.set_speed (Player::INIT_SPEED);
	player.set_tempo (Player::INIT_TEMPO);
	player.set_bar_time (Player::INIT_TIME_HIGH, Player::INIT_TIME_LOW);

	GTK_instr_nbr = 1;
	GTK_sample_nbr = 1;
	GTK_mix_nbr = 1;
	GTK_fx_preset_nbr = 1;
	GTK_fx_real_time_mode_flag = true;
	GTK_octave = 3;
	GTK_line_step = 1;

	/* Reinitialise les mixages */
	player.reset_mix_parameters ();
	
	GTK_modified_flag = false;

	return (0);
}



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