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

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

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



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

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"file.h"
#include	"gtracker.h"
#include	"log.h"
#include	"memory.h"
#include	"Sample.h"
#include	"splstruc.h"
#include	"splhandl.h"
#include	"WaveForm.h"

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



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



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



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



/*\\\ DEFINITION DES VARIABLES DE CLASSE PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ METHODES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise                                             */
/*      Parametres en entree:                                               */
/*        - number: numero du sample a creer.                               */
/*==========================================================================*/

Sample::Sample (int number)
{
	_number = number;

	init ();
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Detruit                                                */
/*==========================================================================*/

Sample::~Sample (void)
{
	restore ();
}



/*==========================================================================*/
/*      Nom: check_ok                                                       */
/*      Description: Verifie l'intergrite de l'objet.                       */
/*      Retour: 0 si l'objet parait correct.                                */
/*==========================================================================*/

signed int	Sample::check_ok (void) const
{
	if (this == NULL)
	{
		LOG_printf ("Sample::check_ok: Error: \"this\" pointer is NULL.\n");
		return (-1);
	}

	if (_data_ptr == NULL)
	{
		LOG_printf ("Sample::check_ok: Error: \"_data_ptr\" pointer is NULL.\n");
		return (-1);
	}

	if (_filename_0 == NULL)
	{
		LOG_printf ("Sample::check_ok: Error: \"_filename_0\" pointer is NULL.\n");
		return (-1);
	}

	if (_loopbuf_ptr == NULL)
	{
		LOG_printf ("Sample::check_ok: Error: \"_loopbuf_ptr\" pointer is NULL.\n");
		return (-1);
	}

	MCHECK (_data_ptr);
	MCHECK (_filename_0);
	MCHECK (_loopbuf_ptr);

	if (_type == 1)
	{
		if (_start_buf_ptr == NULL)
		{
			LOG_printf ("Sample::check_ok: Error: \"_start_buf_ptr\" pointer is NULL for a D2D sample.\n");
			return (-1);
		}

		MCHECK (_start_buf_ptr);

		if (_file_ptr == NULL)
		{
			LOG_printf ("Sample::check_ok: Error: \"_file_ptr\" pointer is NULL for a D2D sample.\n");
			return (-1);
		}
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: self_display                                                   */
/*      Description: Affiche "textuellement" le contenu courant de l'objet. */
/*==========================================================================*/

void	Sample::self_display (void) const
{

	/*** A faire ***/

}



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



void		Sample::get_sample_name (char *name_0)
{
	BASE_copy_string (_name, name_0,
							Sample_NAME_LEN, Sample_NAME_LEN);
	name_0 [Sample_NAME_LEN] = 0;
}



void		Sample::set_sample_name (const char *name_0)
{
	BASE_copy_string (name_0, _name,
							strlen (name_0), Sample_NAME_LEN);
}



int		Sample::get_sample_resolution (void)
{
	return (_nbits);
}



signed int	Sample::set_sample_resolution (int nbits)
{
	_nbits = nbits;

	/* Il faut aussi changer la taille memoire allouee aux
	   buffers de repetitions (et de debut si D2D) */
	return (correct_buffer_len ());
}



int		Sample::get_sample_stereo (void)
{
	return (_tracks);
}



signed int	Sample::set_sample_stereo (int tracks)
{
	_tracks = tracks;

	/* Il faut aussi changer la taille memoire allouee aux
	   buffers de repetitions (et de debut si D2D) */
	return (correct_buffer_len ());
}



int		Sample::get_sample_bytes_per_sample (void)
{
	return ((_tracks * _nbits) >> 3);
}



signed int	Sample::get_sample_balance (void)
{
	return (_autobal);
}



void		Sample::set_sample_balance (signed int balance)
{
	_autobal = balance;
}



int		Sample::get_sample_midi_note (void)
{
	return (_midi_note);
}



void		Sample::set_sample_midi_note (int note)
{
	_midi_note = note;
}



int		Sample::get_sample_volume (void)
{
	return (_volume);
}



void		Sample::set_sample_volume (int volume)
{
	_volume = volume;
}



signed int	Sample::get_sample_finetune (void)
{
	return (_finetune);
}



void		Sample::set_sample_finetune (signed int finetune)
{
	_finetune = finetune;
}



long		Sample::get_sample_freq (void)
{
	return (_freq);
}



void		Sample::set_sample_freq (long freq)
{
	_freq = freq;
}



long		Sample::get_sample_length (void)
{
	return (_length);
}



signed int	Sample::set_sample_length (long length)
{
	/* Sample en D2D */
	if (_type == 1)
	{
		int64_t        new_file_len =
			_file_data_offset + _length * get_sample_bytes_per_sample ();
		int            drive = *(get_sample_d2d_filename ()) - 'A';
		int64_t        free_disk_space;
		int64_t        total_disk_space;
		if (FILE_get_free_disk_space (drive, &free_disk_space, &total_disk_space))
		{
			/* Impossible de connaitre l'espace disk disponible,
				on fait comme si on en avait suffisemment. */
			free_disk_space = new_file_len;
		}
		int64_t        sup_mem = long (new_file_len - FILE_get_file_length (_file_ptr));
		if (   sup_mem > 0
		    && sup_mem < free_disk_space)
		{
			LOG_printf ("Sample::set_sample_length: Error: not enough free disk space.\n");
			return (-1);	/* Erreur: pas assez d'espace disk */
		}

		if (FILE_change_file_length  (_file_ptr, new_file_len))
		{
			LOG_printf ("Sample::set_sample_length: Error: couldn't change file length.\n");
			return (-1);	/* Erreur: impossible de changer la taille du fichier */
		}
	}

	/* Sample en memoire */
	else
	{
		size_t      real_len =
			size_t (length) * get_sample_bytes_per_sample ();
		real_len = MAX (real_len, 1);
		void *      temp_ptr = REALLOC (_data_ptr, real_len);
		if (temp_ptr == NULL)
		{
			LOG_printf ("Sample::set_sample_length: Error: couldn't change sample length.\n");
			return (-1);	/* Erreur: impossible d'agrandir le chunk */
		}
		_data_ptr = temp_ptr;

		/* Si on agrandit, on remplit le supplement par des 0. */
		if (length > _length)
		{
			memset (
				(BYTE *)_data_ptr + size_t (_length) * get_sample_bytes_per_sample (),
				0,
				size_t (length - _length) * get_sample_bytes_per_sample ()
			);
		}
	}

	if (_length != length)
	{
		_view.h_zoom = Sample_H_ZOOM_TO_INIT;
	}

	_length = length;

	/* Corrige la boucle si necessaire */
	set_sample_loop (_loop, _repeat, _replen);
	make_buffers ();

	check_se_view ();

	return (0);
}



long		Sample::get_sample_repeat (void)
{
	return (_repeat);
}



long		Sample::get_sample_replen (void)
{
	return (_replen);
}



long		Sample::get_sample_loop (void)
{
	return (_loop);
}



void		Sample::set_sample_loop (int loop_type, long reppos, long replen)
{
	if (loop_type != WaveForm_LOOP_TYPE_NONE)
	{
		if (_length > 0)
		{
			reppos = MIN (reppos, _length - 1);
			reppos = MAX (reppos, 0);
			replen = MIN (replen, _length - reppos);
			replen = MAX (replen, 1);
		}
		else
		{
			loop_type = WaveForm_LOOP_TYPE_NONE;
		}
	}

	_loop = loop_type;
	_repeat = reppos;
	_replen = replen;
}



long		Sample::get_sample_loopbuf (void)
{
	return (_loopbuf);
}



signed int	Sample::set_sample_loopbuf (long loopbuf)
{
	long		real_len;
	void		*temp_ptr;

	real_len = loopbuf * get_sample_bytes_per_sample ();
	real_len = MAX (real_len, 1);

	temp_ptr = REALLOC (_loopbuf_ptr, real_len);
	if (temp_ptr == NULL)
	{
		LOG_printf ("Sample::set_sample_loopbuf: Error: couldn't change loop buffer length.\n");
		return (-1);	/* Erreur: impossible d'agrandir le chunk */
	}
	_loopbuf_ptr = temp_ptr;
	_loopbuf = loopbuf;

	/* Recalcule les buffers */
	make_buffers ();

	return (0);
}



void	*Sample::get_sample_loopbuf_adr (void)
{
	return (_loopbuf_ptr);
}



void	*Sample::get_sample_data_adr (void)
{
	return (_data_ptr);
}



void	Sample::set_sample_group (int group)
{
	assert (group >= 0);

	_group = group;
}



int	Sample::get_sample_group () const
{
	return (_group);
}



int	Sample::get_sample_type (void)
{
	return (_type);
}



FILE	*Sample::get_sample_file_ptr (void)
{
	return (_file_ptr);
}



void	Sample::set_sample_file_ptr (FILE *file_ptr)
{
	_file_ptr = file_ptr;
}



long	Sample::get_sample_buffer_len (int buffer)
{
	return (_buffer_len [buffer]);
}



signed int	Sample::set_sample_d2d_buffers (long sbuffer_len, long buffer1_len, long buffer2_len)
{
	long		real_len;
	void		*temp_ptr;

	real_len = sbuffer_len * get_sample_bytes_per_sample ();
	real_len = MAX (real_len, 1);

	temp_ptr = REALLOC (_start_buf_ptr, real_len);
	if (temp_ptr == NULL)
	{
		LOG_printf ("Sample::set_sample_loopbuf: Error: couldn't change start buffer length.\n");
		return (-1);	/* Erreur: impossible d'agrandir le chunk */
	}
	_start_buf_ptr = temp_ptr;

	_buffer_len [0] = sbuffer_len;
	_buffer_len [1] = buffer1_len;
	_buffer_len [2] = buffer2_len;

	/* Recalcule les buffers */
	make_buffers ();

	return (0);
}



void		*Sample::get_sample_startbuf_ptr (void)
{
	return (_start_buf_ptr);
}



int64_t	Sample::get_sample_file_data_offset (void)
{
	return (_file_data_offset);
}



void		Sample::set_sample_file_data_offset (int64_t pos)
{
	_file_data_offset = pos;
}



bool		Sample::get_read_d2d_header_flag (void)
{
	return (_header_flag);
}



void		Sample::set_read_d2d_header_flag (bool flag)
{
	_header_flag = flag;
}



const char	*Sample::get_sample_d2d_filename (void)
{
	return (_filename_0);
}



signed int	Sample::set_sample_d2d_filename (const char *filename_0)
{
	FREE (_filename_0);
	_filename_0 = STRDUP (filename_0);
	if (_filename_0 == NULL)
	{
		LOG_printf ("Sample::set_sample_d2d_filename: Error: couldn't reserve memory for filename.\n");
		return (-1);
	}

	return (0);
}



signed int	Sample::use_file_for_d2d (const char *filename_0)
{
	FILE		*file_ptr;

	/* Deja en D2D ? */
	if (_type == 1)
	{
		fclose (_file_ptr);
		_file_ptr = NULL;
	}
	
	/* Prend les infos du fichier */
	file_ptr = FILE_fopen (filename_0, "r+b");

	if (file_ptr == NULL)
	{
		LOG_printf ("Sample::use_file_for_d2d: Error: couldn't open sample file in r+b mode.\n");
		kill_sample ();
		return (-1);	/* Erreur: impossible d'ouvrir le fichier */
	}
	_file_ptr = file_ptr;

	if (read_d2d_header ())
	{
//		LOG_printf ("Sample::use_file_for_d2d: Error: couldn't read file header, or inappropriate sample format.\n");
		fclose (file_ptr);
		_file_ptr = NULL;
		kill_sample ();
		return (-1);	/* Erreur: impossible de modifier les infos du chunk */
	}

	if (set_sample_d2d_filename (filename_0))
	{
		LOG_printf ("Sample::use_file_for_d2d: Error: couldn't store sample filename in memory.\n");
		fclose (file_ptr);
		_file_ptr = NULL;
		kill_sample ();
		return (-1);	/* Erreur lors de la modification du nom */
	}

	return (0);
}



signed int	Sample::read_d2d_header (void)
{
	int		format;
	long		real_len;
	void		*temp_ptr;
	FILE		*file_ptr;
	SPLS_PRIMARY_INFO	sample_info;

/*______________________________________________
 *
 * Recherche des informations necessaires
 *______________________________________________
 */

	/* Remplit le bloc d'info primaires avec les infos actuelles du sample */
	SPLS_init_primary_info (&sample_info, _number);
	sample_info.signed_flag = true;

	/* Recherche les informations primaires contenues dans le header */
	file_ptr = _file_ptr;
	format = SPLS_get_file_format (file_ptr);
	if (format < 0)
	{
		LOG_printf ("Sample::read_d2d_header: Error: couldn't get sample file format.\n");
		return (-1);	/* Erreur lors de la recherche de format du sample */
	}
	if (SPLS_get_primary_info (file_ptr, format, &sample_info))
	{
		LOG_printf ("Sample::read_d2d_header: Error: couldn't get information on the sample file.\n");
		return (-1);	/* Erreur lors de l'identification du sample */
	}

	/* Verification de la validite du sample pour le D2D */
	if (sample_info.acm_flag)
	{
//		LOG_printf ("Sample::read_d2d_header: Error: sample file is compressed.\n");
		return (-1);	/* Pas bon pour le D2D */
	}
	if (sample_info.pack_type != SPLS_PACK_TYPE_PCM)
	{
//		LOG_printf ("Sample::read_d2d_header: Error: sample file is not in the PCM format.\n");
		return (-1);	/* Pas bon pour le D2D */
	}
	if (   sample_info.nbits == 16
		 && sample_info.byte_order != OS_BYTE_ORDER)
	{
//		LOG_printf ("Sample::read_d2d_header: Error: 16 bit sample file is not in the right Little or Big Endian format.\n");
		return (-1);	/* Pas bon pour le D2D */
	}
	if (! sample_info.signed_flag)
	{
//		LOG_printf ("Sample::read_d2d_header: Error: sample data not signed.\n");
		return (-1);	/* Pas bon pour le D2D */
	}

/*______________________________________________
 *
 * Modifie le chunk en consequence
 *______________________________________________
 */

	/* Si on change de longueur, il faut sans doute changer le zoom du sample */
	if (_length != sample_info.length)
	{
		_view.h_zoom = Sample_H_ZOOM_TO_INIT;
	}

	/* Met le chunk au bon format */
	if (set_sample_resolution (sample_info.nbits))
	{
		LOG_printf ("Sample::read_d2d_header: Error: couldn't change sample resolution.\n");
		return (-1);
	}

	if (set_sample_stereo (sample_info.tracks))
	{
		LOG_printf ("Sample::read_d2d_header: Error: couldn't change the number of channels.\n");
		return (-1);
	}

	_freq = sample_info.freq;
	_length = sample_info.length;
	_repeat = sample_info.repeat;
	_replen = sample_info.replen;
	_loop = sample_info.loop_type;
	_finetune = sample_info.finetune;
	_midi_note = sample_info.midi_note;

	check_se_view ();

	if (_type != 1)
	{
		_buffer_len [0] = GTK_default_d2d_buffer_len;
		_buffer_len [1] = GTK_default_d2d_buffer_len;
		_buffer_len [2] = GTK_default_d2d_buffer_len;
		real_len = _buffer_len [0] * get_sample_bytes_per_sample ();
		real_len = MAX (real_len, 1);

		_start_buf_ptr = MALLOC (real_len);
		if (_start_buf_ptr == NULL)
		{
			LOG_printf ("Sample::read_d2d_header: Error: couldn't allocate memory for start buffer.\n");
			return (-1);
		}

		_type = 1;
		_header_flag = false;
	}
	else
	{
		real_len = _buffer_len [0] * get_sample_bytes_per_sample ();
		real_len = MAX (real_len, 1);

		temp_ptr = REALLOC (_start_buf_ptr, real_len);
		if (temp_ptr == NULL)
		{
			LOG_printf ("Sample::read_d2d_header: Error: couldn't change start buffer length.\n");
			return (-1);
		}
		_start_buf_ptr = temp_ptr;
	}

	_file_data_offset = sample_info.data_offset;

	temp_ptr = REALLOC (_data_ptr, 1);
	if (temp_ptr == NULL)
	{
		LOG_printf ("Sample::read_d2d_header: Error: couldn't change sample length.\n");
		return (-1);
	}
	_data_ptr = temp_ptr;

	/* Construit les buffers de repetition et de D2D */
	if (make_buffers ())
	{
		LOG_printf ("Sample::read_d2d_header: Error: couldn't make Direct-To-Disk buffers.\n");
		return (-1);
	}

	return (0);
}



signed int	Sample::update_d2d_header (void)
{

	/*** A faire ***/

	return (0);
}



signed int	Sample::make_buffers (void)
{
	long		sample_mul;
	long		size;
	long		size_2;
	long		counter;
	void		*s_ptr;
	void		*d_ptr;
	FILE		*file_ptr;

	sample_mul = get_sample_bytes_per_sample ();

	/* Pas de donnees */
	if (_length <= 0)
	{
		memset (_loopbuf_ptr, 0, (long)_loopbuf * sample_mul);
		return (0);
	}
	
	/* Buffer de D2D de debut */
	if (_type == 1)
	{
		file_ptr = _file_ptr;
		if (FILE_fseek (file_ptr, _file_data_offset, SEEK_SET))
		{
			LOG_printf ("Sample::make_buffers: Error: couldn't set file position (sample beginning).\n");
			return (-1);
		}
		size = _buffer_len [0];
		size = MIN (size, _length);
		if (fread (_start_buf_ptr, sample_mul * size, 1, file_ptr) != 1)
		{
			LOG_printf ("Sample::make_buffers: Error: couldn't read file (sample beginning).\n");
			return (-1);
		}
	}

	/* Boucle normale */
	if (_loop == WaveForm_LOOP_TYPE_FWD)
	{
		size = MIN (_replen, _loopbuf);
		if (_type == 1)
		{
			if (FILE_fseek (
				file_ptr,
			   _file_data_offset + int64_t (_repeat) * sample_mul,
			   SEEK_SET
			))
			{
				LOG_printf ("Sample::make_buffers: Error: couldn't set file position (forward loop).\n");
				return (-1);
			}
			if ((LWORD) fread (_loopbuf_ptr, sample_mul, size, file_ptr) != size)
			{
				LOG_printf ("Sample::make_buffers: Error: couldn't read file (forward loop).\n");
				return (-1);
			}
		}
		else
		{
			s_ptr = (BYTE *)_data_ptr + _repeat * sample_mul;
			memcpy (_loopbuf_ptr, s_ptr, sample_mul * size);
		}
	}

	/* Ping-pong loop */
	else if (_loop == WaveForm_LOOP_TYPE_PP)
	{
		size = MIN (_replen, _loopbuf);
		if (_type == 1)
		{
			if (FILE_fseek (
				file_ptr,
				  _file_data_offset
				+ int64_t (_repeat + _replen - size) * sample_mul,
				SEEK_SET
			))
			{
				LOG_printf ("Sample::make_buffers: Error: couldn't set file position (ping-pong loop).\n");
				return (-1);
			}
			if ((LWORD) fread (_loopbuf_ptr, sample_mul, size, file_ptr) != size)
			{
				LOG_printf ("Sample::make_buffers: Error: couldn't read file (ping-pong loop).\n");
				return (-1);
			}
		}
		else
		{
			s_ptr =   (BYTE *)_data_ptr
					  + (_repeat + _replen - size) * sample_mul;
			memcpy (_loopbuf_ptr, s_ptr, sample_mul * size);
		}

		if (sample_mul == 1)
		{
			MEM_invert_memory_byte (_loopbuf_ptr, size);
		}
		else if (sample_mul == 2)
		{
			MEM_invert_memory_word (_loopbuf_ptr, size);
		}
		else if (sample_mul == 4)
		{
			MEM_invert_memory_lword (_loopbuf_ptr, size);
		}

		if (size < _loopbuf)
		{
			size_2 = MIN (size, _loopbuf - size);
			d_ptr = (BYTE *)_loopbuf_ptr + _replen * sample_mul;
			s_ptr = (void *)((BYTE *)d_ptr - sample_mul);
			size += size_2;
			SPLH_copy_mem_invert_sample (d_ptr, s_ptr, size_2, sample_mul);
		}
	}

	/* Pas de bouclage */
	else
	{
		size = 1;
		if (_length > 0)
		{
			if (_type == 1)
			{
				if (FILE_fseek (
					file_ptr,
				   _file_data_offset + int64_t (_length - 1) * sample_mul,
				   SEEK_SET
				))
				{
					LOG_printf ("Sample::make_buffers: Error: couldn't set file position (no loop).\n");
					return (-1);
				}
				if (fread (_loopbuf_ptr, sample_mul * size, 1, file_ptr) != 1)
				{
					LOG_printf ("Sample::make_buffers: Error: couldn't read file (no loop).\n");
					return (-1);
				}
			}
			else
			{
				s_ptr = (BYTE *)_data_ptr + (_length - 1) * sample_mul;
				memcpy (_loopbuf_ptr, s_ptr, sample_mul);
			}
		}
		else
		{
			memset (_loopbuf_ptr, 0, sample_mul);
		}
	}

	/* Fin du remplissage du buffer de bouclage */
	counter = _loopbuf - size;
	s_ptr = _loopbuf_ptr;
	d_ptr = (BYTE *)_loopbuf_ptr + size * sample_mul;
	while (counter >= size)
	{
		memcpy (d_ptr, s_ptr, size * sample_mul);
		d_ptr = (BYTE *)d_ptr + size * sample_mul;
		counter -= size;
		size *= 2;
	}
	if (counter > 0)
	{
		memcpy (d_ptr, s_ptr, counter * sample_mul);
	}

	return (0);
}



signed int	Sample::kill_sample (void)
{
	restore ();
	
	return (init ());
}



long	Sample::estimate_memory_usage () const
{
	long				mem = sizeof (*this);

	const long		frame_size = (_nbits >> 3) * _tracks;
	mem += frame_size * _loopbuf;
	if (_type == 1)
	{
		const long		total_buf_len =
			_buffer_len [0] + _buffer_len [1] + _buffer_len [2];
		mem += frame_size * total_buf_len;
	}
	else
	{
		mem += frame_size * _length;
	}

	return (mem);
}



void	Sample::get_sample_view (Sample_VIEW &view)
{
	view = _view;
}



void	Sample::set_sample_view (const Sample_VIEW &view)
{
	_view = view;
	check_se_view ();
}



void	Sample::get_sample_selection (Sample_SELECTION &selection)
{
	selection = _sel;
}



void	Sample::set_sample_selection (const Sample_SELECTION &selection)
{
	_sel = selection;
	check_se_view ();
}



/****************************************************************************/
/*                                                                          */
/*      ROUTINES DE CONVERSION                                              */
/*                                                                          */
/****************************************************************************/



signed int	Sample::convert_sample_8_2_16 (void)
{
	long		real_len;
	void		*temp_ptr;

	if (_nbits != 8)
	{
		LOG_printf ("Sample::convert_sample_8_2_16: Error: this is not a 8 bit sample.\n");
		return (-1);
	}

/*______________________________________________
 *
 * Sample sur disque
 *______________________________________________
 */

	if (_type == 1)
	{
		real_len = _buffer_len [0] * _tracks * 2;
		real_len = MAX (real_len, 1);

		/* Conversion du fichier */
		int64_t        new_file_length =
			_file_data_offset + int64_t (_length) * _tracks * 2;
		if (FILE_change_file_length  (_file_ptr, new_file_length))
		{
			LOG_printf ("Sample::convert_sample_8_2_16: Error: couldn't change file length.\n");
			return (-1);
		}

		if (SPLH_convert_file_8_2_16 (_file_ptr, _file_data_offset, _length * _tracks))
		{
			LOG_printf ("Sample::convert_sample_8_2_16: Error: couldn't convert file.\n");
			return (-1);
		}

		set_sample_resolution (16);

		/* Update le header */
		if (_header_flag)
		{
			update_d2d_header ();
		}
	}

/*______________________________________________
 *
 * Sample en memoire
 *______________________________________________
 */

	else
	{
		real_len = _length * _tracks * 2;
		real_len = MAX (real_len, 1);

		temp_ptr = REALLOC (_data_ptr, real_len);
		if (temp_ptr == NULL)
		{
			LOG_printf ("Sample::convert_sample_8_2_16: Error: couldn't change sample length.\n");
			return (-1);
		}
		_data_ptr = temp_ptr;

		/* Conversion du buffer */
		SPLH_convert_mem_8_2_16 (_data_ptr, _length * _tracks);

		set_sample_resolution (16);
	}

	make_buffers ();
	check_se_view ();

	return (0);
}



signed int	Sample::convert_sample_16_2_8 (void)
{
	long		real_len;
	void		*temp_ptr;

	if (_nbits != 16)
	{
		LOG_printf ("Sample::convert_sample_16_2_8: Error: this is not a 16 bit sample.\n");
		return (-1);
	}

/*______________________________________________
 *
 * Sample sur disque
 *______________________________________________
 */

	if (_type == 1)
	{
		/* Conversion du fichier */
		if (SPLH_convert_file_16_2_8 (_file_ptr, _file_data_offset, _length * _tracks))
		{
			LOG_printf ("Sample::convert_sample_16_2_8: Error: couldn't convert file.\n");
			return (-1);
		}

		int64_t        new_file_length =
			_file_data_offset + int64_t (_length) * _tracks;
		if (FILE_change_file_length  (_file_ptr, new_file_length))
		{
			LOG_printf ("Sample::convert_sample_16_2_8: Error: couldn't change file length.\n");
			return (-1);
		}

		set_sample_resolution (8);

		/* Update le header */
		if (_header_flag)
		{
			update_d2d_header ();
		}
	}

/*______________________________________________
 *
 * Sample en memoire
 *______________________________________________
 */

	else
	{
		/* Conversion du buffer */
		SPLH_convert_mem_16_2_8 (_data_ptr, _length * _tracks);

		real_len = _length * _tracks;
		real_len = MAX (real_len, 1);

		temp_ptr = REALLOC (_data_ptr, real_len);
		if (temp_ptr == NULL)
		{
			LOG_printf ("Sample::convert_sample_16_2_8: Error: couldn't change sample length.\n");
			return (-1);
		}
		_data_ptr = temp_ptr;

		_nbits = 8;
	}

	make_buffers ();
	check_se_view ();

	return (0);
}



signed int	Sample::convert_sample_mono_2_stereo (void)
{
	long		real_len;
	void		*temp_ptr;

	if (_tracks != 1)
	{
		LOG_printf ("Sample::convert_sample_mono_2_stereo: Error: this is not a mono sample.\n");
		return (-1);
	}

/*______________________________________________
 *
 * Sample sur disque
 *______________________________________________
 */

	if (_type == 1)
	{
		/* Conversion du fichier */
		int64_t        new_file_length = 
			_file_data_offset + int64_t (_length) * (_nbits >> 3) * 2;
		if (FILE_change_file_length  (_file_ptr, new_file_length))
		{
			LOG_printf ("Sample::convert_sample_mono_2_stereo: Error: couldn't change file length.\n");
			return (-1);
		}

		if (SPLH_convert_file_mono_2_stereo (_file_ptr, _file_data_offset, _length, _nbits >> 3))
		{
			LOG_printf ("Sample::convert_sample_mono_2_stereo: Error: couldn't convert file.\n");
			return (-1);
		}

		set_sample_stereo (2);

		/* Update le header */
		if (_header_flag)
		{
			update_d2d_header ();
		}
	}

/*______________________________________________
 *
 * Sample en memoire
 *______________________________________________
 */

	else
	{
		real_len = _length * (_nbits >> 3) * 2;
		real_len = MAX (real_len, 1);

		temp_ptr = REALLOC (_data_ptr, real_len);
		if (temp_ptr == NULL)
		{
			LOG_printf ("Sample::convert_sample_mono_2_stereo: Error: couldn't change sample length.\n");
			return (-1);
		}
		_data_ptr = temp_ptr;

		/* Conversion du buffer */
		SPLH_convert_mem_mono_2_stereo (_data_ptr, _length, _nbits >> 3);

		set_sample_stereo (2);
	}

	make_buffers ();
	check_se_view ();

	return (0);
}



signed int	Sample::convert_sample_stereo_2_mono (void)
{
	long		real_len;
	void		*temp_ptr;

	if (_tracks != 2)
	{
		LOG_printf ("Sample::convert_sample_stereo_2_mono: Error: this is not a stereo sample.\n");
		return (-1);
	}

/*______________________________________________
 *
 * Sample sur disque
 *______________________________________________
 */

	if (_type == 1)
	{
		/* Conversion du fichier */
		if (SPLH_convert_file_stereo_2_mono (_file_ptr, _file_data_offset, _length, _nbits >> 3))
		{
			LOG_printf ("Sample::convert_sample_stereo_2_mono: Error: couldn't convert file.\n");
			return (-1);
		}

		int64_t        new_file_length =
			_file_data_offset + int64_t (_length) * (_nbits >> 3);
		if (FILE_change_file_length  (_file_ptr, new_file_length))
		{
			LOG_printf ("Sample::convert_sample_stereo_2_mono: Error: couldn't change file length.\n");
			return (-1);
		}

		set_sample_stereo (1);

		/* Update le header */
		if (_header_flag)
		{
			update_d2d_header ();
		}
	}

/*______________________________________________
 *
 * Sample en memoire
 *______________________________________________
 */

	else
	{
		/* Conversion du buffer */
		SPLH_convert_mem_stereo_2_mono (_data_ptr, _length, _nbits >> 3);

		real_len = _length * (_nbits >> 3);
		real_len = MAX (real_len, 1);

		temp_ptr = REALLOC (_data_ptr, real_len);
		if (temp_ptr == NULL)
		{
			LOG_printf ("Sample::convert_sample_stereo_2_mono: Error: couldn't change sample length.\n");
			return (-1);
		}
		_data_ptr = temp_ptr;

		set_sample_stereo (1);
	}

	make_buffers ();
	check_se_view ();

	return (0);
}



/*\\\ METHODES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



signed int	Sample::init (void)
{
	memset (_name, ' ', Sample_NAME_LEN);
	_type = 0;
	_nbits = Sample_INIT_INIT_NBR_BYTES << 3;
	_tracks = Sample_INIT_INIT_NBR_TRACKS;
	_autobal = -1;
	_volume = 0x100;
	_finetune = 0;
	_loop = WaveForm_LOOP_TYPE_NONE;
	_midi_note = Sample_REF_C2;
	_freq = 44100;
	_length = 0;
	_repeat = 0;
	_replen = 0;
	_loopbuf = Sample_INIT_LOOPBUF_LEN;
	_filename_0 = STRDUP ("");
	_data_ptr = MALLOC (1);
	_loopbuf_ptr = MALLOC (  _loopbuf
	                       * Sample_INIT_INIT_NBR_BYTES
	                       * Sample_INIT_INIT_NBR_TRACKS);
	_group = 0;

	_buffer_len [0] = GTK_default_d2d_buffer_len;
	_buffer_len [1] = GTK_default_d2d_buffer_len;
	_buffer_len [2] = GTK_default_d2d_buffer_len;
	_header_flag = true;
	_file_data_offset = 0;
	_start_buf_ptr = NULL;
	_file_ptr = NULL;

	_view.h_pos = 0;
	_view.h_zoom = Sample_H_ZOOM_TO_INIT;
	_view.v_pos = 0.0f;
	_view.v_zoom = 1.0f;

	_sel.flag = false;
	_sel.start = 0;
	_sel.length = 0;
	_sel.start_channel = 0;
	_sel.nbr_channel = 1;

	return (0);
}



void	Sample::restore (void)
{
	if (_type == 1 && _file_ptr != NULL)
	{
		fclose (_file_ptr);
		_file_ptr = NULL;
	}

	if (_data_ptr != NULL)
	{
		FREE (_data_ptr);
		_data_ptr = NULL;
	}

	if (_filename_0 != NULL)
	{
		FREE (_filename_0);
		_filename_0 = NULL;
	}

	if (_loopbuf_ptr != NULL)
	{
		FREE (_loopbuf_ptr);
		_loopbuf_ptr = NULL;
	}

	if (_start_buf_ptr != NULL)
	{
		FREE (_start_buf_ptr);
		_start_buf_ptr = NULL;
	}
}



/* Corrige la taille des buffers fixes (loop et debut de sample) en
fonction de la taille d'une frame de sample */

signed int	Sample::correct_buffer_len (void)
{
	long		real_len;
	void		*temp_ptr;

	real_len = _loopbuf * get_sample_bytes_per_sample ();
	real_len = MAX (real_len, 1);
	temp_ptr = REALLOC (_loopbuf_ptr, real_len);
	if (temp_ptr == NULL)
	{
		LOG_printf ("Sample::correct_buffer_len: Error: couldn't change loop buffer length.\n");
		return (-1);
	}
	_loopbuf_ptr = temp_ptr;

	if (_type == 1)
	{
		real_len = _buffer_len [0] * get_sample_bytes_per_sample ();
		real_len = MAX (real_len, 1);
		temp_ptr = REALLOC (_start_buf_ptr, real_len);
		if (temp_ptr == NULL)
		{
			LOG_printf ("Sample::correct_buffer_len: Error: couldn't change start buffer length.\n");
			return (-1);
		}
		_start_buf_ptr = temp_ptr;
	}

	return (0);
}



void	Sample::check_se_view (void)
{
	_view.h_pos = MIN (_view.h_pos, _length - 1);
	_view.h_pos = MAX (_view.h_pos, 0);
	_view.v_zoom = MIN (_view.v_zoom, Sample_V_ZOOM_MAX);
	_view.v_zoom = MAX (_view.v_zoom, Sample_V_ZOOM_MIN);
	_view.v_pos = MIN (_view.v_pos, 1.0f - 1.0f / _view.v_zoom);
	_view.v_pos = MAX (_view.v_pos, -1.0f + 1.0f / _view.v_zoom);

	if (_view.h_zoom != Sample_H_ZOOM_TO_INIT)
	{
		_view.h_zoom = MIN (_view.h_zoom, Sample_H_ZOOM_MAX);
		_view.h_zoom = MAX (_view.h_zoom, Sample_H_ZOOM_MIN);
	}

	if (_sel.flag)
	{
		if (_length <= 0)
		{
			_sel.flag = false;
		}
		else
		{
			_sel.start = MIN (_sel.start, _length - 1);
			_sel.start = MAX (_sel.start, 0);
			_sel.length = MIN (_sel.length, _length - _sel.start);
			_sel.length = MAX (_sel.length, 1);
			_sel.start_channel = MIN (_sel.start_channel, _tracks - 1);
			_sel.start_channel = MAX (_sel.start_channel, 0);
			_sel.nbr_channel = MIN (_sel.nbr_channel, _tracks - _sel.start_channel);
			_sel.nbr_channel = MAX (_sel.nbr_channel, 1);
		}
	}
}



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



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