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

GRAOUMF TRACKER 2

Copyright (c) 1996 - 2002 Laurent de Soras

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

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

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

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



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

#include "archi.h"
#include "base.h"
#include "base_ct.h"
#include "d2d.h"
#include "edstring.h"
#include	"file.h"
#include "gtracker.h"
#include "intrface.h"
#include	"log.h"
#include "memory.h"
#include	"Mutex.h"
#include "mpannel.h"
#include "Popup.h"
#include "rsc01.h"
#include "samp.h"
#include "Sample.h"
#include "splstruc.h"
#include	"Thread.h"

#include <cassert>
#include <cstdio>
#include	<cstdlib>



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



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

typedef struct
{
	BYTE		*data_ptr;		// Pointeur sur les donnees, NULL si le buffer n'est pas utilise
	int		sample;			// Sample utilisant le buffer
	long		position;		// Position dans le buffer (en samples)
	long		length;			// Longueur du buffer (en samples)
	bool		write_flag;		// Indique que la zone va etre modifiee et qu'il va faloir la sauvegarder.
} SAMP_ACCESS_BUFFER;



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



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



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

Sample	*SAMP_sample_ptr [1 + GTK_NBRSAMPLES_TOTAL];

SAMP_ACCESS_BUFFER SAMP_access_buffer [SAMP_NBR_ACCESS_BUFFERS];
std::string	SAMP_group_name_arr [1 + SAMP_NBRGROUPS_MAXI];



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



signed int	SAMP_init (void)
{
	for (int spl_cnt = 0; spl_cnt <= GTK_NBRSAMPLES_TOTAL; spl_cnt ++)
	{
		SAMP_sample_ptr [spl_cnt] = new Sample (spl_cnt);
		if (SAMP_sample_ptr [spl_cnt] == NULL)
		{
			LOG_printf ("SAMP_init: couldn't create sample # %d.\n", spl_cnt);
			return (-1);
		}
		if (SAMP_sample_ptr [spl_cnt]->check_ok ())
		{
			LOG_printf ("SAMP_init: failed to create sample # %d.\n", spl_cnt);
			return (-1);
		}
	}

	for (int buf_cnt = 0; buf_cnt < SAMP_NBR_ACCESS_BUFFERS; buf_cnt ++)
	{
		SAMP_access_buffer [buf_cnt].data_ptr = NULL;
	}

	return (0);
}



void	SAMP_restore (void)
{
	SAMP_flush_access_buffers ();

	for (int spl_cnt = 0; spl_cnt <= GTK_NBRSAMPLES_TOTAL; spl_cnt ++)
	{
		if (SAMP_sample_ptr [spl_cnt] != NULL)
		{
			delete SAMP_sample_ptr [spl_cnt];
			SAMP_sample_ptr [spl_cnt] = NULL;
		}
	}
}



/****************************************************************************/
/*                                                                          */
/*      ROUTINES DE BASE                                                    */
/*                                                                          */
/****************************************************************************/



/* name_0 pointe sur une zone deja reservee, suffisante pour nom + 0 */
void	SAMP_get_sample_name (int sample, char *name_0)
{
	SAMP_sample_ptr [sample]->get_sample_name (name_0);
}



void	SAMP_set_sample_name (int sample, const char *name_0)
{
	SAMP_sample_ptr [sample]->set_sample_name (name_0);
}



int	SAMP_get_sample_resolution (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_resolution ());
}



void	SAMP_set_sample_resolution (int sample, int nbits)
{
	SAMP_sample_ptr [sample]->set_sample_resolution (nbits);
}



int SAMP_get_sample_stereo (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_stereo ());
}




void	SAMP_set_sample_stereo (int sample, int tracks)
{
	SAMP_sample_ptr [sample]->set_sample_stereo (tracks);
}



signed int SAMP_get_sample_balance (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_balance ());
}




void	SAMP_set_sample_balance (int sample, signed int balance)
{
	SAMP_sample_ptr [sample]->set_sample_balance (balance);
}



int SAMP_get_sample_midi_note (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_midi_note ());
}




void	SAMP_set_sample_midi_note (int sample, int note)
{
	SAMP_sample_ptr [sample]->set_sample_midi_note (note);
}



int SAMP_get_sample_volume (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_volume ());
}




void	SAMP_set_sample_volume (int sample, int volume)
{
	SAMP_sample_ptr [sample]->set_sample_volume (volume);
}



signed int SAMP_get_sample_finetune (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_finetune ());
}




void	SAMP_set_sample_finetune (int sample, signed int finetune)
{
	SAMP_sample_ptr [sample]->set_sample_finetune (finetune);
}



long SAMP_get_sample_freq (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_freq ());
}




void	SAMP_set_sample_freq (int sample, long freq)
{
	SAMP_sample_ptr [sample]->set_sample_freq (freq);
}



/* Le sample doit etre de type 1 */
long	SAMP_get_sample_file_data_offset (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_file_data_offset ());
}



/* Le sample doit etre de type 1 */
void	SAMP_set_sample_file_data_offset (int sample, long pos)
{
	SAMP_sample_ptr [sample]->set_sample_file_data_offset (pos);
}



/* Le sample doit etre de type 1 */
bool	SAMP_get_read_d2d_header_flag (int sample)
{
	return (SAMP_sample_ptr [sample]->get_read_d2d_header_flag ());
}



/* Le sample doit etre de type 1 */
void	SAMP_set_read_d2d_header_flag (int sample, bool flag)
{
	SAMP_sample_ptr [sample]->set_read_d2d_header_flag (flag);
}



long	SAMP_get_sample_repeat (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_repeat ());
}



long	SAMP_get_sample_replen (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_replen ());
}



long	SAMP_get_sample_loopbuf (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_loopbuf ());
}



FILE	*SAMP_get_sample_file_ptr (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_file_ptr ());
}



void	SAMP_set_sample_file_ptr (int sample, FILE *file_ptr)
{
	SAMP_sample_ptr [sample]->set_sample_file_ptr (file_ptr);
}



long	SAMP_get_sample_buffer_len (int sample, int buffer)
{
	return (SAMP_sample_ptr [sample]->get_sample_buffer_len (buffer));
}



long	SAMP_get_sample_loop (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_loop ());
}



void	*SAMP_get_sample_data_adr (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_data_adr ());
}



void	*SAMP_get_sample_loopbuf_adr (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_loopbuf_adr ());
}



/* Le sample doit etre de type 1 */
void	*SAMP_get_sample_startbuf_ptr (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_startbuf_ptr ());
}



long	SAMP_get_sample_length (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_length ());
}



void	SAMP_set_sample_group (int sample, int group)
{
	SAMP_sample_ptr [sample]->set_sample_group (group);
}



int	SAMP_get_sample_group (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_group ());
}



int	SAMP_get_sample_type (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_type ());
}



int	SAMP_get_sample_bytes_per_sample (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_bytes_per_sample ());
}



signed int	SAMP_set_sample_length (int sample, long length)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->set_sample_length (length));
}



void	SAMP_set_sample_loop (int sample, int loop_type, long reppos, long replen)
{
	SAMP_sample_ptr [sample]->set_sample_loop (loop_type, reppos, replen);
}



signed int	SAMP_set_sample_loopbuf (int sample, long loopbuf)
{
	return (SAMP_sample_ptr [sample]->set_sample_loopbuf (loopbuf));
}



signed int	SAMP_set_sample_d2d_buffers (int sample, long sbuffer_len, long buffer1_len, long buffer2_len)
{
	return (SAMP_sample_ptr [sample]->set_sample_d2d_buffers (sbuffer_len, buffer1_len, buffer2_len));
}



const char	*SAMP_get_sample_d2d_filename (int sample)
{
	return (SAMP_sample_ptr [sample]->get_sample_d2d_filename ());
}



signed int	SAMP_set_sample_d2d_filename (int sample, const char *filename_0)
{
	return (SAMP_sample_ptr [sample]->set_sample_d2d_filename (filename_0));
}



/*==========================================================================*/
/*      Nom: SAMP_read_d2d_header                                           */
/*      Description: Lit a partir du fichier le header du sample et met     */
/*                   a jour les informations du sample.                     */
/*                   Le fichier de D2D doit deja etre ouvert.               */
/*                   Le type est change si necessaire, le chunk est mis a   */
/*                   la bonne taille et tous les buffers sont recalcules.   */
/*                   Si conversion type0 -> type1, le nom du sample est     */
/*                   initialise comme chaine vide (donc a remplir apres).   */
/*      Parametres en entree:                                               */
/*        - sample: numero du sample.                                       */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SAMP_read_d2d_header (int sample)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->read_d2d_header ());
}



/*==========================================================================*/
/*      Nom: SAMP_update_d2d_header                                         */
/*      Description: Met a jour le header d'un fichier de sample en         */
/*                   fonction des donnees contenues dans le descripteur de  */
/*                   samples.                                               */
/*      Parametres en entree:                                               */
/*        - sample: Numero du sample (type 1).                              */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SAMP_update_d2d_header (int sample)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->update_d2d_header ());
}



/*==========================================================================*/
/*      Nom: SAMP_make_buffers                                              */
/*      Description: Remplit les buffers lies au sample: blouclage ou D2D.  */
/*                   La taille des buffers et des boucles doit etre valide, */
/*                   ainsi que les informations les concernant.             */
/*      Parametres en entree:                                               */
/*        - sample: numero du sample a boucler/bufferiser.                  */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SAMP_make_buffers (int sample)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->make_buffers ());
}



/*==========================================================================*/
/*      Nom: SAMP_use_file_for_d2d                                          */
/*      Description: Utilise un fichier deja existant pour creer un sample  */
/*                   Direct-To-Disk utilisant le fichier en question.       */
/*                   L'ancien sample est converti en D2D si necessaire.     */
/*                   S'il etait deja en D2D, l'ancien sample est ferme.     */
/*                   Si une erreur se produit, le sample est detruit.       */
/*      Parametres en entree:                                               */
/*        - sample: numero du sample concerne.                              */
/*        - filename_0: pointeur sur le nom du fichier a utiliser.          */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SAMP_use_file_for_d2d (int sample, const char *filename_0)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->use_file_for_d2d (filename_0));
}



BYTE	*SAMP_get_access_buffer_ptr (int sample, long position, long length, int buffer_id, bool write_flag, bool peak_file_flag)
{
	/* Sample sur disque */	
	if (SAMP_get_sample_type (sample) == 1)
	{
		if (SAMP_access_buffer [buffer_id].data_ptr != NULL)
		{
			if (SAMP_release_access_buffer (buffer_id))
			{
				LOG_printf ("SAMP_get_access_buffer_ptr: Error: couldn't release old access buffer.\n");
				return (NULL);
			}
		}
		
		SAMP_access_buffer [buffer_id].data_ptr = (BYTE *) MALLOC (  SAMP_get_sample_bytes_per_sample (sample)
		                                                           * length);
		if (SAMP_access_buffer [buffer_id].data_ptr == NULL)
		{
			LOG_printf ("SAMP_get_access_buffer_ptr: Error: couldn't allocate memory for data.\n");
			return (NULL);					
		}
		
		/* Attend que le gestionaire de cache ait fini son travail */
		GTK_mutex.wait ();

		if (fseek (SAMP_get_sample_file_ptr (sample), 
		             SAMP_get_sample_bytes_per_sample (sample) * position
		           + SAMP_get_sample_file_data_offset (sample),
		           SEEK_SET) != 0)
		{
			LOG_printf ("SAMP_get_access_buffer_ptr: Error: couldn't change file position.\n");
			GTK_mutex.signal ();
			return (NULL);
		}
		if (fread (SAMP_access_buffer [buffer_id].data_ptr, 
		           SAMP_get_sample_bytes_per_sample (sample) * length, 1,
		           SAMP_get_sample_file_ptr (sample)) != 1)
		{
			LOG_printf ("SAMP_get_access_buffer_ptr: Error: couldn't read data in file.\n");
			GTK_mutex.signal ();
			return (NULL);				
		}

		/* Fait repartir le gestionaire de cache */
		GTK_mutex.signal ();

		SAMP_access_buffer [buffer_id].length = length;
		SAMP_access_buffer [buffer_id].position = position;
		SAMP_access_buffer [buffer_id].sample = sample;
		SAMP_access_buffer [buffer_id].write_flag = write_flag;

		return (SAMP_access_buffer [buffer_id].data_ptr);
	}

	/* Sample en memoire */	
	return (  (BYTE *) SAMP_get_sample_data_adr (sample)
	        + position * SAMP_get_sample_bytes_per_sample (sample));
}



signed int	SAMP_release_access_buffer (int buffer_id)
{
	int		sample;

	if (SAMP_access_buffer [buffer_id].data_ptr != NULL)
	{
		/* On doit ecrire le contenu precedent sur le disque */
		if (SAMP_access_buffer [buffer_id].write_flag)
		{
			sample = SAMP_access_buffer [buffer_id].sample;

			/* Attend que le gestionaire de cache ait fini son travail */
			GTK_mutex.wait ();

			if (fseek (SAMP_get_sample_file_ptr (sample), 
				      SAMP_get_sample_bytes_per_sample (sample)
					 * SAMP_access_buffer [buffer_id].position
					 + SAMP_get_sample_file_data_offset (sample),
					 SEEK_SET) != 0)
			{
				LOG_printf ("SAMP_release_access_buffer: Error: couldn't change file position.\n");
				GTK_mutex.signal ();
				return (-1);
			}
			if (fwrite (SAMP_access_buffer [buffer_id].data_ptr, 
				   SAMP_get_sample_bytes_per_sample (sample)
				 * SAMP_access_buffer [buffer_id].length, 1,
				 SAMP_get_sample_file_ptr (sample)) != 1)
			{
				LOG_printf ("SAMP_release_access_buffer: Error: couldn't write data in file.\n");
				GTK_mutex.signal ();
				return (-1);				
			}

			/* Fait repartir le gestionaire de cache */
			GTK_mutex.signal ();
		}

		FREE (SAMP_access_buffer [buffer_id].data_ptr);
		SAMP_access_buffer [buffer_id].data_ptr = NULL;
	}

	return (0);
}



signed int	SAMP_flush_access_buffers (void)
{
	for (int buf_cnt = 0; buf_cnt < SAMP_NBR_ACCESS_BUFFERS; buf_cnt ++)
	{
		if (SAMP_release_access_buffer (buf_cnt))
		{
			LOG_printf ("SAMP_flush_access_buffers: Error: couldn't flush buffer # %d.\n",
			            buf_cnt);
			return (-1);
		}
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SAMP_kill_sample                                               */
/*      Description: Efface et remet a 0 un sample                          */
/*      Parametres en entree:                                               */
/*        - sample: numero du sample concerne.                              */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SAMP_kill_sample (int sample)
{
	signed int	ret_val;

	SAMP_flush_access_buffers ();

	/* Attend que le gestionaire de cache ait fini son travail */
	GTK_mutex.wait ();

	ret_val = SAMP_sample_ptr [sample]->kill_sample ();

	/* Fait repartir le gestionaire de cache */
	GTK_mutex.signal ();
	
	return (ret_val);
}



/*==========================================================================*/
/*      Nom: SAMP_clear_samples                                             */
/*      Description: Efface tous les samples                                */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SAMP_clear_samples (void)
{
	GTK_stop_all ();

	for (int sample = GTK_NBRSAMPLES_TOTAL; sample >= 1; sample --)
	{
		if (SAMP_kill_sample (sample))
		{
			LOG_printf ("SAMP_clear_samples: Error: couldn't kill sample # %d.\n",
			            sample);
			return (-1);	/* Erreur lors de la destruction du sample */
		}
	}

	SAMP_clear_groups ();

	return (0);
}



long	SAMP_estimate_memory_usage (int sample)
{
	return (SAMP_sample_ptr [sample]->estimate_memory_usage ());
}



void	SAMP_get_sample_view (int sample, Sample_VIEW &view)
{
	SAMP_sample_ptr [sample]->get_sample_view (view);
}



void	SAMP_set_sample_view (int sample, const Sample_VIEW &view)
{
	SAMP_sample_ptr [sample]->set_sample_view (view);
}



void	SAMP_get_sample_selection (int sample, Sample_SELECTION &selection)
{
	SAMP_sample_ptr [sample]->get_sample_selection (selection);
}



void	SAMP_set_sample_selection (int sample, const Sample_SELECTION &selection)
{
	SAMP_sample_ptr [sample]->set_sample_selection (selection);
}



/****************************************************************************/
/*                                                                          */
/*      SAMPLE GROUPS                                                       */
/*                                                                          */
/****************************************************************************/



void	SAMP_clear_groups ()
{
	for (int group = 1; group <= SAMP_NBRGROUPS_MAXI; ++group)
	{
		SAMP_group_name_arr [group].clear ();
	}
}



const std::string &	SAMP_get_group_name (int group)
{
	assert (group > 0);
	assert (group <= SAMP_NBRGROUPS_MAXI);

	return (SAMP_group_name_arr [group]);
}



void	SAMP_set_group_name (int group, const char *name_0)
{
	assert (group > 0);
	assert (group <= SAMP_NBRGROUPS_MAXI);
	assert (name_0 != 0);

	SAMP_group_name_arr [group] = name_0;
}



/****************************************************************************/
/*                                                                          */
/*      CONVERSIONS                                                         */
/*                                                                          */
/****************************************************************************/



signed int	SAMP_convert_sample_8_2_16 (int sample)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->convert_sample_8_2_16 ());
}



signed int	SAMP_convert_sample_16_2_8 (int sample)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->convert_sample_16_2_8 ());
}



signed int	SAMP_convert_sample_mono_2_stereo (int sample)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->convert_sample_mono_2_stereo ());
}



signed int	SAMP_convert_sample_stereo_2_mono (int sample)
{
	SAMP_flush_access_buffers ();

	return (SAMP_sample_ptr [sample]->convert_sample_stereo_2_mono ());
}



signed int	SAMP_check_all_samples (void)
{
	int		i;
	signed int	res;

	for (i = 1; i < GTK_NBRSAMPLES_TOTAL; i ++)
	{
		res = SAMP_sample_ptr [i]->check_ok () < 0 ? -1 : res;
	}

	return (res);
}



/****************************************************************************/
/*                                                                          */
/*      ROUTINES AVEC UTILISATION DE L'INTERFACE                            */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: SAMP_set_sample_volume_intr                                    */
/*      Description: Change le volume du sample courant de                  */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*                2 = Entree au clavier,                                    */
/*                3 = Entree au pop-up.                                     */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	SAMP_set_sample_volume_intr (int type, signed int value)
{
	int		new_value;
	signed int	line;
	signed long	code;
	char		nbr3_0 [3+1];
	Popup		volume_popup;

	new_value = SAMP_get_sample_volume (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		new_value += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr3_0, "%3X", new_value);
		EDIT_edit_string_base (nbr3_0, RSC_OBJ_MP_SUBM_SAMPLES_MAIN_VOL_VAL, 3, 16);
		new_value = (int) strtol (nbr3_0, NULL, 16);
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_value = value;
		break;

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		volume_popup.add_line ("400 (+12 dB)", 0x400);
		volume_popup.add_line ("300 (+10 dB)", 0x300);
		volume_popup.add_line ("200 ( +6 dB)", 0x200);
		volume_popup.add_line ("180 ( +4 dB)", 0x180);
		volume_popup.add_line ("100 (  0 dB)", 0x100);
		volume_popup.add_line (" C0 ( -4 dB)", 0x0C0);
		volume_popup.add_line (" 80 ( -6 dB)", 0x080);
		volume_popup.add_line (" 60 (-10 dB)", 0x060);
		volume_popup.add_line (" 40 (-12 dB)", 0x040);

		line = volume_popup.select_radio_by_code (new_value);
		code = volume_popup.manage (line);
		if (code >= 0)
		{
			new_value = (int) code;
		}

		break;
	}

	new_value = MIN (new_value, 0xFFF);
	new_value = MAX (new_value, 0);
	SAMP_set_sample_volume (GTK_sample_nbr, new_value);
	GTK_modified_flag = true;
	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: SAMP_set_sample_length_intr                                    */
/*      Description: Change la longueur du sample courant, de               */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	SAMP_set_sample_length_intr (int type, signed long value)
{
	unsigned long	spl_pos;

	spl_pos = SAMP_get_sample_length (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		spl_pos += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		if (EDIT_edit_splpos (RSC_OBJ_MP_SUBM_SAMPLES_MAIN_LEN_VAL,
									 &spl_pos,
									 SAMP_get_sample_freq (GTK_sample_nbr),
									 SAMP_get_sample_stereo (GTK_sample_nbr),
									 SAMP_get_sample_resolution (GTK_sample_nbr) >> 3) != 0)
		{
			INTR_pattern_editor_menu_ptr->refresh ();
			return;
		}
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		spl_pos = value;
		break;
	}

	spl_pos = MAX (spl_pos, 0);
	INTR_graph_ptr->mouse_bee ();
	if (SAMP_set_sample_length (GTK_sample_nbr, spl_pos))
	{
		INTR_graph_ptr->mouse_arrow ();
		INTR_dialog_box ("CHANGE LENGTH",
							  "Can't change lenght of the sample.",
							  "Cancel", 0, 0);
	}
	else
	{
		GTK_modified_flag = true;
	}
	INTR_graph_ptr->mouse_arrow ();
	MPAN_display_status (true);
	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: SAMP_set_sample_repeat_intr                                    */
/*      Description: Change la position de repetion du sample courant, de   */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*        - wait_flag: true si l'action doit etre retardee (en cas de       */
/*                     bouton d'action continue), false si effet immediat.  */
/*==========================================================================*/

void	SAMP_set_sample_repeat_intr (int type, signed long value, bool wait_flag)
{
	signed long	spl_pos;

	spl_pos = SAMP_get_sample_repeat (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		spl_pos += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		if (EDIT_edit_splpos (RSC_OBJ_MP_SUBM_SAMPLES_MAIN_REPEAT_VAL,
									 (unsigned long *) (&spl_pos),
									 SAMP_get_sample_freq (GTK_sample_nbr),
									 SAMP_get_sample_stereo (GTK_sample_nbr),
									 SAMP_get_sample_resolution (GTK_sample_nbr) >> 3) != 0)
		{
			INTR_pattern_editor_menu_ptr->refresh ();
			return;
		}
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		spl_pos = value;
		break;
	}

	spl_pos = MIN (spl_pos, SAMP_get_sample_length (GTK_sample_nbr) - SAMP_get_sample_replen (GTK_sample_nbr));
	spl_pos = MAX (spl_pos, 0);
	SAMP_set_sample_loop (GTK_sample_nbr, SAMP_get_sample_loop (GTK_sample_nbr),
	                      spl_pos, SAMP_get_sample_replen (GTK_sample_nbr));
	GTK_modified_flag = true;

	/* Operation retardee */
	if (wait_flag)
	{
		if (INTR_waiting_operation_type != INTR_WAITOP_SPL_RPOS)
		{
			INTR_do_waiting_op (true);
		}
		INTR_waiting_operation_type = INTR_WAITOP_SPL_RPOS;
	}
	else
	{
		INTR_graph_ptr->mouse_bee ();
		SAMP_make_buffers (GTK_sample_nbr);
		INTR_graph_ptr->mouse_arrow ();
	}

	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: SAMP_set_sample_replen_intr                                    */
/*      Description: Change la longueur de repetion du sample courant, de   */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*        - wait_flag: true si l'action doit etre retardee (en cas de       */
/*                     bouton d'action continue), false si effet immediat.  */
/*==========================================================================*/

void	SAMP_set_sample_replen_intr (int type, signed long value, bool wait_flag)
{
	signed long	spl_pos;

	spl_pos = SAMP_get_sample_replen (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		spl_pos += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		if (EDIT_edit_splpos (RSC_OBJ_MP_SUBM_SAMPLES_MAIN_REPLEN_VAL,
									 (unsigned long *) (&spl_pos),
									 SAMP_get_sample_freq (GTK_sample_nbr),
									 SAMP_get_sample_stereo (GTK_sample_nbr),
									 SAMP_get_sample_resolution (GTK_sample_nbr) >> 3) != 0)
		{
			INTR_pattern_editor_menu_ptr->refresh ();
			return;
		}
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		spl_pos = value;
		break;
	}

	spl_pos = MIN (spl_pos, SAMP_get_sample_length (GTK_sample_nbr) - SAMP_get_sample_repeat (GTK_sample_nbr));
	spl_pos = MAX (spl_pos, 1);
	SAMP_set_sample_loop (GTK_sample_nbr, SAMP_get_sample_loop (GTK_sample_nbr),
	                      SAMP_get_sample_repeat (GTK_sample_nbr), spl_pos);
	GTK_modified_flag = true;

	/* Operation retardee */
	if (wait_flag)
	{
		if (INTR_waiting_operation_type != INTR_WAITOP_SPL_RLEN)
		{
			INTR_do_waiting_op (true);
		}
		INTR_waiting_operation_type = INTR_WAITOP_SPL_RLEN;
	}
	else
	{
		INTR_graph_ptr->mouse_bee ();
		SAMP_make_buffers (GTK_sample_nbr);
		INTR_graph_ptr->mouse_arrow ();
	}

	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: SAMP_set_sample_loopbuf_intr                                   */
/*      Description: Change la longueur du buffer de rep du sample, de      */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	SAMP_set_sample_loopbuf_intr (int type, signed long value)
{
	signed long	spl_pos;

	spl_pos = SAMP_get_sample_loopbuf (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		spl_pos += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		if (EDIT_edit_splpos (RSC_OBJ_MP_SUBM_SAMPLES_MAIN_RBUF_VAL,
									 (unsigned long *) (&spl_pos),
									 SAMP_get_sample_freq (GTK_sample_nbr),
									 SAMP_get_sample_stereo (GTK_sample_nbr),
									 SAMP_get_sample_resolution (GTK_sample_nbr) >> 3) != 0)
		{
			INTR_pattern_editor_menu_ptr->refresh ();
			return;
		}
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		spl_pos = value;
		break;
	}

	spl_pos = MAX (MIN (spl_pos, SPLS_RBUF_MAXLEN), SPLS_RBUF_MINLEN);
	SAMP_set_sample_loopbuf (GTK_sample_nbr, spl_pos);
	INTR_graph_ptr->mouse_bee ();
	SAMP_make_buffers (GTK_sample_nbr);
	INTR_graph_ptr->mouse_arrow ();
	GTK_modified_flag = true;
	MPAN_display_status (true);
	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: SAMP_set_sample_midi_note_intr                                 */
/*      Description: Change la note midi du sample courant, de              */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	SAMP_set_sample_midi_note_intr (int type, signed int value)
{
	signed int	value2;

	value2 = SAMP_get_sample_midi_note (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		value2 += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:

		/*** A faire ***/

		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		value2 = value;
		break;
	}

	value2 = MAX (MIN (value2, GTK_NBRNOTES_MAXI - 1), 0);
	SAMP_set_sample_midi_note (GTK_sample_nbr, value2);
	GTK_modified_flag = true;
	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: SAMP_set_sample_finetune_intr                                  */
/*      Description: Change le finetune du sample courant, de               */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	SAMP_set_sample_finetune_intr (int type, signed int value)
{
	signed int	value2;

	value2 = SAMP_get_sample_finetune (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		value2 += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:

		/*** A faire ***/

		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		value2 = value;
		break;
	}

	value2 = MAX (MIN (value2, 7), -8);
	SAMP_set_sample_finetune (GTK_sample_nbr, value2);
	GTK_modified_flag = true;
	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: SAMP_set_sample_d2d_buffers_intr                               */
/*      Description: Change la longueur d'un buffer du sample D2D courant,  */
/*                   de differentes manieres. L'interface est ici mise en   */
/*                   jeu.                                                   */
/*      Parametres en entree:                                               */
/*        - buffer_num: numero du buffer concerne (0, 1 ou 2)               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	SAMP_set_sample_d2d_buffers_intr (int buffer_num, int type, signed long value)
{
	unsigned long	spl_pos;
	int		edit_object [3] =
	{
		RSC_OBJ_MP_SUBM_SAMPLES_D2D_SBUF_VAL,
		RSC_OBJ_MP_SUBM_SAMPLES_D2D_MBUF1_VAL,
		RSC_OBJ_MP_SUBM_SAMPLES_D2D_MBUF2_VAL
	};

	if (SAMP_get_sample_type (GTK_sample_nbr) != 1)
	{
		return;
	}

	spl_pos = SAMP_get_sample_buffer_len (GTK_sample_nbr, buffer_num);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		spl_pos += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		if (EDIT_edit_splpos (edit_object [buffer_num],
									 &spl_pos,
									 SAMP_get_sample_freq (GTK_sample_nbr),
									 SAMP_get_sample_stereo (GTK_sample_nbr),
									 SAMP_get_sample_resolution (GTK_sample_nbr) >> 3) != 0)
		{
			INTR_pattern_editor_menu_ptr->refresh ();
			return;
		}
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		spl_pos = value;
		break;
	}

	spl_pos = MAX (spl_pos, SPLS_D2DBUF_MINLEN);

	INTR_graph_ptr->mouse_bee ();
	if (PatEdMenuSamplesD2d::same_len_flag)
	{
		SAMP_set_sample_d2d_buffers (GTK_sample_nbr, spl_pos, spl_pos, spl_pos);
	}
	else
	{
		switch (buffer_num)
		{
		case	2:
			SAMP_set_sample_d2d_buffers (GTK_sample_nbr,
												 SAMP_get_sample_buffer_len (GTK_sample_nbr, 0),
												 SAMP_get_sample_buffer_len (GTK_sample_nbr, 1),
												 spl_pos);
			break;
		case	1:
			SAMP_set_sample_d2d_buffers (GTK_sample_nbr,
												 SAMP_get_sample_buffer_len (GTK_sample_nbr, 0),
												 spl_pos,
												 SAMP_get_sample_buffer_len (GTK_sample_nbr, 2));
			break;
		case	0:
		default:
			SAMP_set_sample_d2d_buffers (GTK_sample_nbr,
												 spl_pos,
												 SAMP_get_sample_buffer_len (GTK_sample_nbr, 1),
												 SAMP_get_sample_buffer_len (GTK_sample_nbr, 2));
			break;
		}
	}
	SAMP_make_buffers (GTK_sample_nbr);
	INTR_graph_ptr->mouse_arrow ();

	GTK_modified_flag = true;
	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: SAMP_set_file_data_offset_intr                                 */
/*      Description: Change la position des donnees du sample courant, de   */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*        - wait_flag: true si l'action doit etre retardee (en cas de       */
/*                     bouton d'action continue), false si effet immediat.  */
/*==========================================================================*/

void	SAMP_set_file_data_offset_intr (int type, signed long value, bool wait_flag)
{
	signed long	spl_pos;
	long		file_length;
	long		sample_blength;
	char		nbr8_0 [8+1];

	if (SAMP_get_sample_type (GTK_sample_nbr) != 1)
	{
		return;
	}
	spl_pos = SAMP_get_sample_file_data_offset (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		spl_pos += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr8_0, "%8ld", spl_pos);
		if (EDIT_edit_string_base (nbr8_0, RSC_OBJ_MP_SUBM_SAMPLES_D2D_DOFFSET_VAL, 8, 10))
		{
			INTR_pattern_editor_menu_ptr->refresh ();
			return;
		}
		spl_pos = strtol (nbr8_0, NULL, 10);
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		spl_pos = value;
		break;
	}

	file_length = FILE_get_file_length (SAMP_get_sample_file_ptr (GTK_sample_nbr));
	sample_blength =   SAMP_get_sample_length (GTK_sample_nbr)
						  * SAMP_get_sample_bytes_per_sample (GTK_sample_nbr);
	spl_pos = MIN (spl_pos, file_length - sample_blength);
	spl_pos = MAX (spl_pos, 0);
	SAMP_set_sample_file_data_offset (GTK_sample_nbr, spl_pos);
	GTK_modified_flag = true;

	/* Operation retardee */
	if (wait_flag)
	{
		if (INTR_waiting_operation_type != INTR_WAITOP_SPL_DOFFSET)
		{
			INTR_do_waiting_op (true);
		}
		INTR_waiting_operation_type = INTR_WAITOP_SPL_DOFFSET;
	}
	else
	{
		INTR_graph_ptr->mouse_bee ();
		SAMP_make_buffers (GTK_sample_nbr);
		INTR_graph_ptr->mouse_arrow ();
	}

	INTR_pattern_editor_menu_ptr->refresh ();
}



void	SAMP_set_sample_group_intr (int type, signed int value)
{
	char			string_0 [127+1];
	int			new_value = SAMP_get_sample_group (GTK_sample_nbr);

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		new_value += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (string_0, INTR_base_song_3, new_value);
		EDIT_edit_string_base (
			string_0,
			RSC_OBJ_MP_SUBM_SAMPLES_MAIN_GRP_VAL,
			3,
			INTR_base_song
		);
		new_value = int (strtol (string_0, NULL, INTR_base_song));
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_value = value;
		break;
	}

	new_value = MAX (MIN (new_value, SAMP_NBRGROUPS_MAXI), 0);
	SAMP_set_sample_group (GTK_sample_nbr, new_value);
	GTK_modified_flag = true;
	INTR_pattern_editor_menu_ptr->refresh ();
}



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



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