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

        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	<assert.h>
#include	<math.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"file.h"
#include	"log.h"
#include	"memory.h"
#include	"splhandl.h"
#include	"SWord24.h"
#include	"SWord24.hpp"



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



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



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

inline void	SPLH_clip_sample_24 (SLWORD &sample, SLWORD &peak);



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



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



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



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



/*==========================================================================*/
/*      Nom: SPLH_convert_mem_8_2_16                                        */
/*      Description: Convertit un bloc de memoire 8 bits en 16 bits.        */
/*      Parametres en entree:                                               */
/*        - length: Longueur (en samples) a convertir.                      */
/*      Parametres en entree/sortie:                                        */
/*        - spl_ptr: pointeur sur le sample.                                */
/*==========================================================================*/

void	SPLH_convert_mem_8_2_16 (void *spl_ptr, LWORD length)
{
	SBYTE		*spl_8_ptr;
	SWORD		*spl_16_ptr;

	if (length <= 0)
	{
		return;
	}

	/* On se place a la fin des donnees */
	spl_8_ptr = (SBYTE *) spl_ptr;
	spl_16_ptr = (SWORD *) spl_ptr;
	spl_8_ptr += length;
	spl_16_ptr += length;

	/* Hop transfert */
	while (length > 0)
	{
		*--spl_16_ptr = (*--spl_8_ptr) * 256;
		length --;
	}
}




/*==========================================================================*/
/*      Nom: SPLH_convert_mem_16_2_8                                        */
/*      Description: Convertit un bloc de memoire 16 bits en 8 bits.        */
/*      Parametres en entree:                                               */
/*        - length: Longueur (en samples) a convertir.                      */
/*      Parametres en entree/sortie:                                        */
/*        - spl_ptr: pointeur sur le sample.                                */
/*==========================================================================*/

void	SPLH_convert_mem_16_2_8 (void *spl_ptr, LWORD length)
{
	SBYTE		*spl_8_ptr;
	SWORD		*spl_16_ptr;

	if (length <= 0)
	{
		return;
	}

	spl_8_ptr = (SBYTE *) spl_ptr;
	spl_16_ptr = (SWORD *) spl_ptr;

	/* Hop transfert */
	while (length > 0)
	{
		*spl_8_ptr++ = (SBYTE) ((*spl_16_ptr++) / 256);
		length --;
	}
}




/*==========================================================================*/
/*      Nom: SPLH_convert_file_8_2_16                                       */
/*      Description: Convertit un bloc de fichier 8 bits en 16 bits. La     */
/*                   taille du fichier doit etre suffisante pour accepter   */
/*                   la modifiacation.                                      */
/*      Parametres en entree:                                               */
/*        - length: Longueur (en samples) a convertir.                      */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier.                                  */
/*        - data_offset: offset des donnees dans le fichier, en octets.     */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLH_convert_file_8_2_16 (FILE *file_ptr, int64_t data_offset, LWORD length)
{
	long		buffer_len;
	void		*buffer_ptr;

	if (length <= 0)
	{
		return (0);
	}

	buffer_ptr = MEM_max_malloc (length * 2, 4096, &buffer_len);
	if (buffer_ptr == NULL)
	{
		LOG_printf ("SPLH_convert_file_8_2_16: Error: couldn't allocate a buffer for file conversion.\n");
		return (-1);
	}

	while (length > 0)
	{
		LWORD          sample_len = MIN (length, buffer_len / 2);
		length -= sample_len;

		if (FILE_fseek (file_ptr, data_offset + length, SEEK_SET))
		{
			LOG_printf ("SPLH_convert_file_8_2_16: Error: couldn't position on source.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		if (fread (buffer_ptr, 1, sample_len, file_ptr) != size_t (sample_len))
		{
			LOG_printf ("SPLH_convert_file_8_2_16: Error: couldn't read data.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		SPLH_convert_mem_8_2_16 (buffer_ptr, sample_len);

		if (FILE_fseek (file_ptr, data_offset + int64_t (length) * 2, SEEK_SET))
		{
			LOG_printf ("SPLH_convert_file_8_2_16: Error: couldn't position on destination.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		if ((LWORD) fwrite (buffer_ptr, 2, sample_len, file_ptr) != sample_len)
		{
			LOG_printf ("SPLH_convert_file_8_2_16: Error: couldn't write data.\n");
			return (-1);
		}
	}

	FREE (buffer_ptr);
	if (fflush (file_ptr))
	{
		LOG_printf ("SPLH_convert_file_8_2_16: Error: couldn't flush file buffers.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLH_convert_file_16_2_8                                       */
/*      Description: Convertit un bloc de fichier 16 bits en 8 bits.        */
/*      Parametres en entree:                                               */
/*        - length: Longueur (en samples) a convertir.                      */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier.                                  */
/*        - data_offset: offset des donnees dans le fichier, en octets.     */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLH_convert_file_16_2_8 (FILE *file_ptr, int64_t data_offset, LWORD length)
{
	long		buffer_len;
	long		sample_len;
	long		sample_pos;
	void		*buffer_ptr;

	if (length <= 0)
	{
		return (0);
	}

	buffer_ptr = MEM_max_malloc (length * 2, 4096, &buffer_len);
	if (buffer_ptr == NULL)
	{
		LOG_printf ("SPLH_convert_file_16_2_8: Error: couldn't allocate a buffer for file conversion.\n");
		return (-1);
	}

	sample_pos = 0;
	while (sample_pos < length)
	{
		sample_len = MIN ((length - sample_pos), buffer_len / 2);

		if (FILE_fseek (file_ptr, data_offset + int64_t (sample_pos) * 2, SEEK_SET))
		{
			LOG_printf ("SPLH_convert_file_16_2_8: Error: couldn't position on source.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		if (fread (buffer_ptr, 2, sample_len, file_ptr) != size_t (sample_len))
		{
			LOG_printf ("SPLH_convert_file_16_2_8: Error: couldn't position read data.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		SPLH_convert_mem_16_2_8 (buffer_ptr, sample_len);

		if (FILE_fseek (file_ptr, data_offset + sample_pos, SEEK_SET))
		{
			LOG_printf ("SPLH_convert_file_16_2_8: Error: couldn't position on destination.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		if (fwrite (buffer_ptr, 1, sample_len, file_ptr) != size_t (sample_len))
		{
			LOG_printf ("SPLH_convert_file_16_2_8: Error: couldn't write data.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		sample_pos += sample_len;
	}

	FREE (buffer_ptr);
	if (fflush (file_ptr))
	{
		LOG_printf ("SPLH_convert_file_16_2_8: Error: couldn't flush file buffers.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLH_convert_mem_mono_2_stereo                                 */
/*      Description: Convertit un bloc de memoire mono en stereo. La meme   */
/*                   information est diffusee sur les deux canaux.          */
/*      Parametres en entree:                                               */
/*        - length: Longueur (en samples) a convertir.                      */
/*        - resol: Resolution des samples en octets (1/2 = 8/16 bits).      */
/*      Parametres en entree/sortie:                                        */
/*        - spl_ptr: pointeur sur le sample.                                */
/*==========================================================================*/

void	SPLH_convert_mem_mono_2_stereo (void *spl_ptr, LWORD length, int resol)
{
	SBYTE		*spl_8m_ptr;
	SBYTE		*spl_8s_ptr;
	SBYTE		temp_8;
	SWORD		*spl_16m_ptr;
	SWORD		*spl_16s_ptr;
	SWORD		temp_16;

	if (length <= 0)
	{
		return;
	}

	/* 16 bits */
	if (resol == 2)
	{
		/* On se place a la fin des donnees */
		spl_16m_ptr = (SWORD *) spl_ptr;
		spl_16s_ptr = (SWORD *) spl_ptr;
		spl_16m_ptr += length;
		spl_16s_ptr += length * 2;

		/* Hop transfert */
		while (length > 0)
		{
			temp_16 = *--spl_16m_ptr;
			*--spl_16s_ptr = temp_16;
			*--spl_16s_ptr = temp_16;
			length --;
		}
	}

	/* 8 bits */
	else
	{
		/* On se place a la fin des donnees */
		spl_8m_ptr = (SBYTE *) spl_ptr;
		spl_8s_ptr = (SBYTE *) spl_ptr;
		spl_8m_ptr += length;
		spl_8s_ptr += length * 2;

		/* Hop transfert */
		while (length > 0)
		{
			temp_8 = *--spl_8m_ptr;
			*--spl_8s_ptr = temp_8;
			*--spl_8s_ptr = temp_8;
			length --;
		}
	}
}




/*==========================================================================*/
/*      Nom: SPLH_convert_mem_stereo_2_mono                                 */
/*      Description: Convertit un bloc de memoire stereo en mono, avec      */
/*                   moyenne du volume.                                     */
/*      Parametres en entree:                                               */
/*        - length: Longueur (en samples) a convertir.                      */
/*        - resol: Resolution des samples en octets (1/2 = 8/16 bits).      */
/*      Parametres en entree/sortie:                                        */
/*        - spl_ptr: pointeur sur le sample.                                */
/*==========================================================================*/

void	SPLH_convert_mem_stereo_2_mono (void *spl_ptr, LWORD length, int resol)
{
	SBYTE		*spl_8m_ptr;
	SBYTE		*spl_8s_ptr;
	SWORD		temp_8;
	SWORD		*spl_16m_ptr;
	SWORD		*spl_16s_ptr;
	SWord24	*spl_24m_ptr;
	SWord24	*spl_24s_ptr;
	SLWORD	temp;

	if (length <= 0)
	{
		return;
	}

	/* 24 bits */
	if (resol == 3)
	{
		spl_24m_ptr = (SWord24 *) spl_ptr;
		spl_24s_ptr = (SWord24 *) spl_ptr;

		/* Hop transfert */
		while (length > 0)
		{
			temp = *spl_24s_ptr++;
			temp += *spl_24s_ptr++;
			*spl_24m_ptr++ = (SWord24) (temp / 2);
			length --;
		}
	}

	/* 16 bits */
	else if (resol == 2)
	{
		spl_16m_ptr = (SWORD *) spl_ptr;
		spl_16s_ptr = (SWORD *) spl_ptr;

		/* Hop transfert */
		while (length > 0)
		{
			temp = *spl_16s_ptr++;
			temp += *spl_16s_ptr++;
			*spl_16m_ptr++ = (SWORD) (temp / 2);
			length --;
		}
	}

	/* 8 bits */
	else
	{
		spl_8m_ptr = (SBYTE *) spl_ptr;
		spl_8s_ptr = (SBYTE *) spl_ptr;

		/* Hop transfert */
		while (length > 0)
		{
			temp_8 = *spl_8s_ptr++;
			temp_8 += *spl_8s_ptr++;
			*spl_8m_ptr++ = (SBYTE) (temp_8 / 2);
			length --;
		}
	}
}




/*==========================================================================*/
/*      Nom: SPLH_convert_file_mono_2_stereo                                */
/*      Description: Convertit un bloc de fichier mono en stereo. La        */
/*                   taille du fichier doit etre suffisante pour accepter   */
/*                   la modifiacation. Le volume est ainsi double.          */
/*      Parametres en entree:                                               */
/*        - length: Longueur (en samples) a convertir.                      */
/*        - resol: Resolution des samples en octets (1/2 = 8/16 bits).      */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier.                                  */
/*        - data_offset: offset des donnees dans le fichier, en octets.     */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLH_convert_file_mono_2_stereo (FILE *file_ptr, int64_t data_offset, LWORD length, int resol)
{
	long		buffer_len;
	long		sample_len;
	void		*buffer_ptr;

	if (length <= 0)
	{
		return (0);
	}

	buffer_ptr = MEM_max_malloc (length * resol * 2, 4096, &buffer_len);
	if (buffer_ptr == NULL)
	{
		LOG_printf ("SPLH_convert_file_mono_2_stereo: Error: couldn't allocate a buffer for file conversion.\n");
		return (-1);
	}

	while (length > 0)
	{
		sample_len = MIN (length, buffer_len / (resol * 2));
		length -= sample_len;

		if (FILE_fseek (file_ptr, data_offset + int64_t (length) * resol, SEEK_SET))
		{
			LOG_printf ("SPLH_convert_file_mono_2_stereo: Error: couldn't position on source.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		if (fread (buffer_ptr, resol, sample_len, file_ptr) != size_t (sample_len))
		{
			LOG_printf ("SPLH_convert_file_mono_2_stereo: Error: couldn't read data.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		SPLH_convert_mem_mono_2_stereo (buffer_ptr, sample_len, resol);

		if (FILE_fseek (file_ptr, data_offset + int64_t (length) * resol * 2, SEEK_SET))
		{
			LOG_printf ("SPLH_convert_file_mono_2_stereo: Error: couldn't position on destination.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		if (fwrite (buffer_ptr, resol * 2, sample_len, file_ptr) != size_t (sample_len))
		{
			LOG_printf ("SPLH_convert_file_mono_2_stereo: Error: couldn't write data.\n");
			FREE (buffer_ptr);
			return (-1);
		}
	}

	FREE (buffer_ptr);
	if (fflush (file_ptr))
	{
		LOG_printf ("SPLH_convert_file_mono_2_stereo: Error: couldn't flush file buffer.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLH_convert_file_stereo_2_mono                                */
/*      Description: Convertit un bloc de fichier stereo en mono.           */
/*      Parametres en entree:                                               */
/*        - length: Longueur (en samples) a convertir.                      */
/*        - resol: Resolution des samples en octets (1/2 = 8/16 bits).      */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier.                                  */
/*        - data_offset: offset des donnees dans le fichier, en octets.     */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLH_convert_file_stereo_2_mono (FILE *file_ptr, int64_t data_offset, LWORD length, int resol)
{
	long		buffer_len;
	long		sample_len;
	long		sample_pos;
	void		*buffer_ptr;

	if (length <= 0)
	{
		return (0);
	}

	buffer_ptr = MEM_max_malloc (length * resol * 2, 4096, &buffer_len);
	if (buffer_ptr == NULL)
	{
		LOG_printf ("SPLH_convert_file_stereo_2_mono: Error: couldn't allocate a buffer for file conversion.\n");
		return (-1);
	}

	sample_pos = 0;
	while (sample_pos < length)
	{
		sample_len = MIN ((length - sample_pos), buffer_len / (resol * 2));

		if (FILE_fseek (file_ptr, data_offset + int64_t (sample_pos) * resol * 2, SEEK_SET))
		{
			LOG_printf ("SPLH_convert_file_stereo_2_mono: Error: couldn't position on source.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		if (fread (buffer_ptr, resol * 2, sample_len, file_ptr) != size_t (sample_len))
		{
			LOG_printf ("SPLH_convert_file_stereo_2_mono: Error: couldn't read data.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		SPLH_convert_mem_stereo_2_mono (buffer_ptr, sample_len, resol);

		if (FILE_fseek (file_ptr, data_offset + int64_t (sample_pos) * resol, SEEK_SET))
		{
			LOG_printf ("SPLH_convert_file_stereo_2_mono: Error: couldn't position on destination.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		if (fwrite (buffer_ptr, resol, sample_len, file_ptr) != size_t (sample_len))
		{
			LOG_printf ("SPLH_convert_file_stereo_2_mono: Error: couldn't write data.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		sample_pos += sample_len;
	}

	FREE (buffer_ptr);
	if (fflush (file_ptr))
	{
		LOG_printf ("SPLH_convert_file_stereo_2_mono: Error: couldn't flush file buffer.\n");
		return (-1);
	}

	return (0);
}



void	SPLH_convert_mem_24_2_flt (float dest_ptr [], const SLWORD src_ptr [], long length, long nbr_chn)
{
	assert (src_ptr != 0);
	assert (dest_ptr != 0);
	assert (length > 0);
	assert (nbr_chn > 0);

	const float		mult = 1.0f;	// 1.0f / (1L << 24);

	length *= nbr_chn;

	long				pos = 0;
	do
	{
		dest_ptr [pos] = static_cast <float> (src_ptr [pos]) * mult;
		++ pos;
	}
	while (pos < length);
}



void	SPLH_convert_mem_flt_2_24 (SLWORD dest_ptr [], const float src_ptr [], long length, long nbr_chn)
{
	assert (src_ptr != 0);
	assert (dest_ptr != 0);
	assert (length > 0);
	assert (nbr_chn > 0);

	const float		mult = 1.0f;	// (1L << 24)

	length *= nbr_chn;

	long				pos = 0;
	do
	{
		dest_ptr [pos] = static_cast <SLWORD> (src_ptr [pos] * mult);
		++ pos;
	}
	while (pos < length);
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_8_stereo_2_24_stereo                             */
/*      Description: Copie un sample 8 bits stereo en un samples 24 bits    */
/*                   stereo.                                                */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le sample source.                         */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur le sample destination.                   */
/*==========================================================================*/

void	SPLH_copy_mem_8_stereo_2_24_stereo (SLWORD *dest_ptr, const SBYTE *src_ptr, LWORD length)
{
	long				pos = 0;

	do
	{
		dest_ptr [pos * 2    ] = (SLWORD)(src_ptr [pos * 2    ]) * 65536;
		dest_ptr [pos * 2 + 1] = (SLWORD)(src_ptr [pos * 2 + 1]) * 65536;
		++ pos;
	}
	while (pos < length);
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_8_stereo_2_24_bimono										 */
/*      Description: Copie un sample 8 bits stereo en deux samples 24 bits  */
/*                   mono, chacun reprenant une des deux voies.             */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le sample source.                         */
/*      Parametres en sortie:                                               */
/*        - dest_1_ptr: pointeur sur le sample destination gauche.          */
/*        - dest_2_ptr: pointeur sur le sample destination droit.           */
/*==========================================================================*/

void	SPLH_copy_mem_8_stereo_2_24_bimono (SLWORD *dest_1_ptr, SLWORD *dest_2_ptr, const SBYTE *src_ptr, LWORD length)
{
	long				pos = 0;

	do
	{
		dest_1_ptr [pos] = (SLWORD)(src_ptr [pos * 2    ]) * 65536;
		dest_2_ptr [pos] = (SLWORD)(src_ptr [pos * 2 + 1]) * 65536;
		++ pos;
	}
	while (pos < length);
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_16_stereo_2_24_bimono                            */
/*      Description: Copie un sample 16 bits stereo en deux samples 24 bits */
/*                   mono, chacun reprenant une des deux voies.             */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le sample source.                         */
/*      Parametres en sortie:                                               */
/*        - dest_1_ptr: pointeur sur le sample destination gauche.          */
/*        - dest_2_ptr: pointeur sur le sample destination droit.           */
/*==========================================================================*/

void	SPLH_copy_mem_16_stereo_2_24_bimono (SLWORD *dest_1_ptr, SLWORD *dest_2_ptr, const SWORD *src_ptr, LWORD length)
{
	long				pos = 0;

	do
	{
		dest_1_ptr [pos] = (SLWORD)(src_ptr [pos * 2    ]) * 256;
		dest_2_ptr [pos] = (SLWORD)(src_ptr [pos * 2 + 1]) * 256;
		++ pos;
	}
	while (pos < length);
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_flt_stereo_2_8_stereo                            */
/*      Description: Copie un sample float stereo en un samples 8 bits      */
/*                   stereo.                                                */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le sample source.                         */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur le sample destination.                   */
/*==========================================================================*/

float	SPLH_copy_mem_flt_stereo_2_8_stereo (SBYTE *dest_ptr, const float *src_ptr, LWORD length)
{
	SLWORD			peak = 0;
	long				pos = 0;

	do
	{
		SLWORD			l = SLWORD (floor (src_ptr [pos * 2    ] + 0.5f));
		SLWORD			r = SLWORD (floor (src_ptr [pos * 2 + 1] + 0.5f));
		SPLH_clip_sample_24 (l, peak);
		SPLH_clip_sample_24 (r, peak);
		dest_ptr [pos * 2    ] = (SBYTE)(l >> 16);
		dest_ptr [pos * 2 + 1] = (SBYTE)(r >> 16);

		++ pos;
	}
	while (pos < length);

	return (float (peak));
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_flt_bimono_2_8_stereo                             */
/*      Description: Copie un sample flaot stereo en deux samples 8 bits    */
/*                   mono, chacun reprenant une des deux voies.             */
/*      Parametres en entree:                                               */
/*        - src_1_ptr: pointeur sur le sample source gauche.                */
/*        - src_2_ptr: pointeur sur le sample source droit.                 */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur le sample destination.                   */
/*==========================================================================*/

float	SPLH_copy_mem_flt_bimono_2_8_stereo (SBYTE *dest_ptr, const float *src_1_ptr, const float *src_2_ptr, LWORD length)
{
	SLWORD			peak = 0;
	long				pos = 0;

	do
	{
		SLWORD			l = SLWORD (floor (src_1_ptr [pos] + 0.5f));
		SLWORD			r = SLWORD (floor (src_2_ptr [pos] + 0.5f));
		SPLH_clip_sample_24 (l, peak);
		SPLH_clip_sample_24 (r, peak);
		dest_ptr [pos * 2    ] = (SBYTE)(l >> 16);
		dest_ptr [pos * 2 + 1] = (SBYTE)(r >> 16);

		++ pos;
	}
	while (pos < length);

	return (float (peak));
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_flt_stereo_2_16_stereo                           */
/*      Description: Copie un sample float stereo en un samples 16 bits     */
/*                   stereo.                                                */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le sample source.                         */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur le sample destination.                   */
/*==========================================================================*/

float		SPLH_copy_mem_flt_stereo_2_16_stereo (SWORD *dest_ptr, const float *src_ptr, LWORD length)
{
	SLWORD			peak = 0;
	long				pos = 0;

	do
	{
		SLWORD			l = SLWORD (floor (src_ptr [pos * 2    ] + 0.5f));
		SLWORD			r = SLWORD (floor (src_ptr [pos * 2 + 1] + 0.5f));
		SPLH_clip_sample_24 (l, peak);
		SPLH_clip_sample_24 (r, peak);
		dest_ptr [pos * 2    ] = (SWORD)(l >> 8);
		dest_ptr [pos * 2 + 1] = (SWORD)(r >> 8);

		++ pos;
	}
	while (pos < length);

	return (float (peak));
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_flt_bimono_2_16_stereo                           */
/*      Description: Copie deux samples float mono, chacun reprenant une    */
/*                   des deux voies, en un sample 16 bits stereo.           */
/*      Parametres en entree:                                               */
/*        - src_1_ptr: pointeur sur le sample source gauche.                */
/*        - src_2_ptr: pointeur sur le sample source droit.                 */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur le sample destination.                   */
/*==========================================================================*/

float		SPLH_copy_mem_flt_bimono_2_16_stereo (SWORD *dest_ptr, const float *src_1_ptr, const float *src_2_ptr, LWORD length)
{
	SLWORD			peak = 0;
	long				pos = 0;

	do
	{
		SLWORD			l = SLWORD (floor (src_1_ptr [pos] + 0.5f));
		SLWORD			r = SLWORD (floor (src_2_ptr [pos] + 0.5f));
		SPLH_clip_sample_24 (l, peak);
		SPLH_clip_sample_24 (r, peak);
		dest_ptr [pos * 2    ] = (SWORD)(l >> 8);
		dest_ptr [pos * 2 + 1] = (SWORD)(r >> 8);

		++ pos;
	}
	while (pos < length);

	return (float (peak));
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_flt_stereo_2_24c_stereo                          */
/*      Description: Copie un sample float stereo en un                     */
/*                   samples 24 bits stereo sur 3 octets.                   */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le sample source.                         */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur le sample destination.                   */
/*==========================================================================*/

float	SPLH_copy_mem_flt_stereo_2_24c_stereo (SWord24 *dest_ptr, const float *src_ptr, LWORD length)
{
	SLWORD			peak = 0;
	long				pos = 0;

	do
	{
		SLWORD			l = SLWORD (floor (src_ptr [pos * 2    ] + 0.5f));
		SLWORD			r = SLWORD (floor (src_ptr [pos * 2 + 1] + 0.5f));
		SPLH_clip_sample_24 (l, peak);
		SPLH_clip_sample_24 (r, peak);
		dest_ptr [pos * 2    ] = (SWord24) l;
		dest_ptr [pos * 2 + 1] = (SWord24) r;

		++ pos;
	}
	while (pos < length);

	return (float (peak));
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_24_bimono_2_24c_stereo                           */
/*      Description: Copie deux samples float mono, chacun reprenant une    */
/*                   des deux voies, en un sample 24 bits stereo sur 3      */
/*                   octets.                                                */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le sample source.                         */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur le sample destination.                   */
/*==========================================================================*/

float	SPLH_copy_mem_flt_bimono_2_24c_stereo (SWord24 *dest_ptr, const float *src_1_ptr, const float *src_2_ptr, LWORD length)
{
	SLWORD			peak = 0;
	long				pos = 0;

	do
	{
		SLWORD			l = SLWORD (floor (src_1_ptr [pos] + 0.5f));
		SLWORD			r = SLWORD (floor (src_2_ptr [pos] + 0.5f));
		SPLH_clip_sample_24 (l, peak);
		SPLH_clip_sample_24 (r, peak);
		dest_ptr [pos * 2    ] = (SWord24) l;
		dest_ptr [pos * 2 + 1] = (SWord24) r;

		++ pos;
	}
	while (pos < length);

	return (float (peak));
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_invert_sample                                    */
/*      Description: Copie un sample en l'inversant                         */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le dernier sample du sample source (qui   */
/*                   est lu a l'envers).                                    */
/*        - length: longueur en samples.                                    */
/*        - sample_mul: nombre d'octets par sample.                         */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur la fin du sample destination.            */
/*==========================================================================*/

void	SPLH_copy_mem_invert_sample (void *dest_ptr, const void *src_ptr, LWORD length, int sample_mul)
{
	if (sample_mul == 2)
	{
		for ( ; length > 0; length --)
		{
			*((WORD *)dest_ptr) = *((const WORD *)src_ptr);
			src_ptr = (const void *) ((const BYTE *)src_ptr - 2);
			dest_ptr = (void *) ((BYTE *)dest_ptr + 2);
		}
	}

	else if (sample_mul == 4)
	{
		for ( ; length > 0; length --)
		{
			*((LWORD *)dest_ptr) = *((const LWORD *)src_ptr);
			src_ptr = (const void *) ((const BYTE *)src_ptr - 4);
			dest_ptr = (void *) ((BYTE *)dest_ptr + 4);
		}
	}

	else if (sample_mul == 1)
	{
		for ( ; length > 0; length --)
		{
			*((BYTE *)dest_ptr) = *((const BYTE *)src_ptr);
			src_ptr = (const void *) ((const BYTE *)src_ptr - 1);
			dest_ptr = (void *) ((BYTE *)dest_ptr + 1);
		}
	}

	else if (sample_mul > 0)
	{
		for ( ; length > 0; length --)
		{
			memcpy (dest_ptr, src_ptr, sample_mul);
			src_ptr = (const void *) ((const BYTE *)src_ptr - sample_mul);
			dest_ptr = (void *) ((BYTE *)dest_ptr + sample_mul);
		}
	}
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_invert_convert_sample_2_16                       */
/*      Description: Copie un sample en l'inversant et le convertit en 16   */
/*                   bits. Ne traite que les samples mono ou stereo.        */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le dernier sample du sample source (qui   */
/*                   est lu a l'envers).                                    */
/*        - length: longueur en samples.                                    */
/*        - channels: nombre de voies (toujours 1 ou 2).                    */
/*        - src_resol: resolution du sample source en octets/sample (1 ou   */
/*                     2).                                                  */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur la fin du sample destination.            */
/*==========================================================================*/

void	SPLH_copy_mem_invert_convert_sample_2_16 (SWORD *dest_ptr, const void *src_ptr, LWORD length, int channels, int src_resol)
{
	/* 16 bits */
	if (src_resol == 2)
	{
		if (channels == 1)
		{
			for ( ; length > 0; length --)
			{
				*dest_ptr++ = *((const SWORD *)src_ptr);
				src_ptr = (const void *) ((const SWORD *)src_ptr - 1);
			}
		}

		else if (channels == 2)
		{
			for ( ; length > 0; length --)
			{
				*dest_ptr++ = *((const SWORD *)src_ptr);
				*dest_ptr++ = *((const SWORD *)src_ptr + 1);
				src_ptr = (const void *) ((const SWORD *)src_ptr - 2);
			}
		}
	}

	/* 8 bits */
	else
	{
		if (channels == 1)
		{
			for ( ; length > 0; length --)
			{
				*dest_ptr++ = (SWORD)(*((const SBYTE *)src_ptr)) << 8;
				src_ptr = (const void *) ((const SBYTE *)src_ptr - 1);
			}
		}

		else if (channels == 2)
		{
			for ( ; length > 0; length --)
			{
				*dest_ptr++ = (SWORD)(*((const SBYTE *)src_ptr)) << 8;
				*dest_ptr++ = (SWORD)(*((const SBYTE *)src_ptr + 1)) << 8;
				src_ptr = (const void *) ((const SBYTE *)src_ptr - 2);
			}
		}
	}
}



/*==========================================================================*/
/*      Nom: SPLH_copy_mem_convert_sample_2_16                              */
/*      Description: Copie un sample en memoire en le convertissant en 16   */
/*                   bits.                                                  */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le debut du sample source.                */
/*        - length: longueur en samples.                                    */
/*        - channels: nombre de voies.                                      */
/*        - src_resol: resolution du sample source en octets/sample (1 ou   */
/*                     2).                                                  */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur la fin du sample destination.            */
/*==========================================================================*/

void	SPLH_copy_mem_convert_sample_2_16 (SWORD *dest_ptr, const void *src_ptr, LWORD length, int channels, int src_resol)
{
	/* 16 bits */
	if (src_resol == 2)
	{
		memcpy (dest_ptr, src_ptr, length * channels * 2);
	}

	/* 8 bits */
	else
	{
		for (length *= channels ; length > 0; length --)
		{
			*dest_ptr++ = (SWORD)(*((const SBYTE *)src_ptr)) << 8;
			src_ptr = (const void *) ((const SBYTE *)src_ptr + 1);
		}
	}
}



inline void	SPLH_clip_sample_24 (SLWORD &sample, SLWORD &peak)
{
	if (sample > 0x007FFFFFL)
	{
		if (sample > peak)
		{
			peak = sample;
		}
		sample = 0x007FFFFFL;
	}
	else if (sample < -0x00800000L)
	{
		if (sample < -peak)
		{
			peak = -sample;
		}
		sample = 0x00800000;
	}
}



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