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

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

#include "AssignSpl.h"
#include "archi.h"
#include "base.h"
#include "base_ct.h"
#include "d2d.h"
#include	"filter.h"
#include "gt_limit.h"
#include "gtracker.h"
#include	"log.h"
#include "memory.h"
#include "mixer.h"
#include "mix_fx.h"
#include "os.h"
#include "player.h"
#include "splhandl.h"
#include	"WaveForm.h"



/*\\\ CONSTANTES ET MACROS PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Delay necessaire entre le mixage et le replay effectif (ms). On peut
prendre 100 ms pour Win 95/98 et 500 ms Win NT 4.0 */
#define	MIX_INIT_LATENCY_TIME_MS			500



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

/* Element du tableau de l'ordre de mixage */
typedef struct
{
	WORD		s_track_type;	/* Type de la piste source */
	WORD		s_track_nbr;	/* Numero de la piste source */
	WORD		s_track_index;	/* Index du numero de la piste source dans le descripteur de la piste destination */
	WORD		d_track_type;	/* Type de la piste destination */
	WORD		d_track_nbr;	/* Numero de la piste destination */
	SWORD		s_buf_nbr;		/* Buffer source a utiliser (-1 = sample ou buffer AIN) */
	SWORD		d_buf_nbr;		/* Buffer destination (-1 = master) */
	/* Quand la piste destination est un effet, et qu'on vient de lui mixer sa
	   derniere piste source, les deux valeurs suivante indiquent les numeros
		de buffer dry et wet afin d'appliquer l'effet. */
	WORD		dry_buf_nbr;
	WORD		wet_buf_nbr;
} MIX_TRACK_ORDER;



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

void	MIX_explore_fx_track (int fx_track_nbr, int global_fx_track_nbr, bool track_mixed_to_aou_flag [GTK_NBRTRACKS_MAXI] [GTK_NBROUT_MAXI]);

void	MIX_apply_dest_track_mix_param (LWORD &mix_volume, LWORD &mix_panning, Player::TrackInfo &d_track_info, int d_track_type, int track_index);
void	MIX_set_new_sample_position (int track);

bool	MIX_resample (Player::TrackInfo &track_info, float *d_buffer_ptr, int d_stereo, int voice_index);
void	MIX_mix_track (const float *s_buffer_ptr, float *d_buffer_ptr,
		               LWORD length, int s_stereo, int d_stereo,
		               StereoGain &cr, bool erase_flag, bool proc_audio_flag, bool preserve_stereo_vol_flag);
template <class T>
void	MIX_copy_buffer (float dest_ptr [], const float src_ptr [], long len, int stereo);
template <class T>
void	MIX_copy_buffer (float dest_ptr [], const float src_ptr [], long len, int d_stereo, int s_stereo);



/*\\\ DEFINITION DES VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

LWORD	MIX_replay_freq;		/* Frequence d'echantillonnage du mixer, fixee par le driver */
LWORD	MIX_max_frame_length = MIX_MAX_FRAME_LENGTH;	/* Longueur maximum d'une frame, en samples */
LWORD	MIX_latency_time_ms = MIX_INIT_LATENCY_TIME_MS;	/* Delay necessaire entre le mixage et le replay effectif (ms) */

/* Taille de la zone des buffers intermediaires, en samples */
LWORD	MIX_buffer_zone_size = MIX_INIT_BUFFER_ZONE_SIZE;

/* Longueur utilisee pour les calculs. Fixee par le gestionnaire de partition. */
LWORD	MIX_frame_length;

/* Adresse des buffers finaux, par piste. A initialiser */
float *	MIX_driver_out_buffer_ptr [GTK_NBROUT_MAXI];
float *	MIX_driver_in_buffer_ptr [GTK_NBRIN_MAXI];

/* Largeur de la fenetre du filtre de reconstruction */
int	MIX_recons_filter_width = MIX_INIT_RECONS_FLITER_WIDTH;



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

/* Tableau de mixage */
/* A initialiser */
MIX_TRACK_ORDER	MIX_track_order [GTK_NBRTRACKS_MAXI * GTK_NBROUT_MAXI];
int	MIX_total_nbr_tracks = 0;		/* Nombre de pistes presentes dans le tableau de mixage */

/* Nombre de buffers intermediaires 0 = mono, 1 = stereo */
int	MIX_nbr_intermediate_buffers [2] = { 0, 0 };

/* Adresse de la zone des buffers intermediaires */
float *	MIX_buffer_zone_ptr = NULL;

// Temporary buffers (mono or stereo)
float	MIX_buffer_tmp_proc [MIX_MAX_FRAME_LENGTH * 2];	// For the resampling destination

/* Adresses de chaque buffer intermediaire de mixage */
float *	MIX_int_buffer_ptr [2] [GTK_NBRTRACKS_MAXI * GTK_NBROUT_MAXI];

/* Buffers de mixage finaux */
float		MIX_driver_mix_data [(GTK_NBROUT_MAXI + GTK_NBRIN_MAXI) * MIX_MAX_FRAME_LENGTH];

/* Pistes triees lors de la construction du tableau de mixage */
bool	MIX_sorted_track_flag [Player::NBR_GLOBAL_TRACKS];
struct
{
	WORD		first;
	WORD		last;
} MIX_use_of_track [Player::NBR_GLOBAL_TRACKS];

/* Type de qualite de mixage */
int	MIX_sound_quality = MIX_SOUND_QUALITY_NORMAL;

// Met en solo les samples appartenant au groupe indique.
// 0 = joue tous les samples.
int	MIX_sample_group = 0;



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



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



/*==========================================================================*/
/*      Nom: MIX_init                                                       */
/*      Description: Initialisation des routines de mixage.                 */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MIX_init ()
{
	LOG_printf ("Mixer init:\n");

	/* Reserve la zone pour les buffers intermediaires */
	MIX_buffer_zone_ptr = (float *) MALLOC (MIX_buffer_zone_size * sizeof (float));
	if (MIX_buffer_zone_ptr == NULL)
	{
		LOG_printf ("MIX_init: Error: couldn't reserve memory for mix buffers.\n");
		return (-1);
	}

	LOG_printf ("\tOK.\n");

	return (0);
}



void	MIX_restore ()
{
	if (MIX_buffer_zone_ptr != NULL)
	{
		FREE (MIX_buffer_zone_ptr);
		MIX_buffer_zone_ptr = NULL;
	}
}



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

int	MIX_get_sound_quality ()
{
	return (MIX_sound_quality);
}



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

signed int	MIX_set_sound_quality (int quality)
{
	signed int	ret_val = 0;

	GTK_mutex.wait ();

	Player &			player = Player::use_instance ();

	const bool		hq_flag = (quality > MIX_SOUND_QUALITY_NORMAL);

	for (int track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; track ++)
	{
		const int		global_track_nbr =
			player._track_info_list [Pattern_TYPE_SPL] [track];
		Player::TrackInfo &	track_info = player._track_info [global_track_nbr];

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

			try
			{
				voice.spl.set_hq_interpolator (hq_flag);
			}
			catch (...)
			{
				LOG_printf (
					"MIX_set_sound_quality: Warninig: couldn't create a "
					"reconstruction filter for Sample track # %d.\n",
					track
				);
				ret_val = -1;
			}
		}
	}

	MIX_sound_quality = quality;

	GTK_mutex.signal ();

	return (ret_val);
}



/*==========================================================================*/
/*      Nom: MIX_create_mix_buffers                                         */
/*      Description: Repartit les buffers finaux de mixage (in et out).     */
/*==========================================================================*/

void	MIX_create_mix_buffers ()
{
	int		track;

	const Player &	player = Player::use_instance ();

	for (track = 0; track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; track ++)
	{
		const int		global_track_nbr =
			player._track_info_list [GTK_TRACK_TYPE_AOU] [track];
		const Player::TrackInfo &	track_info = player._track_info [global_track_nbr];
		if (track_info.stereo == 0)
		{
			MIX_driver_out_buffer_ptr [track] = MIX_driver_out_buffer_ptr [track - 1];
		}
		else
		{
			MIX_driver_out_buffer_ptr [track] =   MIX_driver_mix_data
			                                    + MIX_max_frame_length * track;
		}
	}
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_AIN]; track ++)
	{
		const int		global_track_nbr =
			player._track_info_list [Pattern_TYPE_AIN] [track];
		const Player::TrackInfo &	track_info = player._track_info [global_track_nbr];
		if (track_info.stereo == 0)
		{
			MIX_driver_in_buffer_ptr [track] = MIX_driver_in_buffer_ptr [track - 1];
		}
		else
		{
			MIX_driver_in_buffer_ptr [track] =   MIX_driver_mix_data
			                                   +   MIX_max_frame_length
			                                     * (track + GTK_NBROUT_MAXI);
		}
	}
}



/*==========================================================================*/
/*      Nom: MIX_create_mix_array                                           */
/*      Description: Cree le tableau de mixage en parcourant l'arbre forme  */
/*                   par le mixage successif des pistes.                    */
/*==========================================================================*/

void	MIX_create_mix_array ()
{
	int		stereo;
	int		mix;
	int		used_buffer_type;
	int		aou_track;
	int		track_index;
	int		track_type;
	int		track_nbr;
	int		s_global_track_nbr;
	int		d_global_track_nbr;
	int		nbr_free_intermediate_buffers [2];
	int		free_intermediate_buffers [2] [GTK_NBRTRACKS_MAXI * GTK_NBROUT_MAXI];	/* Pile */
	int		*free_int_buf_ptr [2];
	int		int_buf_for_global_track [Player::NBR_GLOBAL_TRACKS] [2];	/* 0 = wet, 1 = dry */
	bool		track_mixed_to_aou_flag [GTK_NBRTRACKS_MAXI] [GTK_NBROUT_MAXI];
	MIX_TRACK_ORDER *	track_order_ptr;

	const Player &	player = Player::use_instance ();

/*______________________________________________
 *
 * Tri des pistes
 *______________________________________________
 */

/*
Principe:
	On scanne les pistes d'effets. Si cette piste utilise des piste qui
ne sont pas encore dans le tableau, on rentre dans ces pistes avant de
les placer. On finit par tomber dans des pistes de samples et d'Audio
In, que l'on place.

Optimisation (faite):
	A chaque fois qu'une piste sert pour la premiere fois, regarder si
elle doit etre mixee dans une piste Audio Out. Dans ce cas, mettre ce
mixage juste a cote de l'endroit ou elle est utilisee. Ca permet
d'economiser les buffers, particulierement pour les pistes de samples
qui servent de source a un effet et a l'Audio In.
*/

	/*** La construction du tableau fait comme si les
		  pistes pouvaient etre gardees en memoire. ***/

	MIX_total_nbr_tracks = 0;
	memset (MIX_sorted_track_flag, 0, Player::NBR_GLOBAL_TRACKS * sizeof (*MIX_sorted_track_flag));
	memset (track_mixed_to_aou_flag, 0, GTK_NBRTRACKS_MAXI * GTK_NBROUT_MAXI * sizeof (**track_mixed_to_aou_flag));

	/* Commence par regarder dans toutes les pistes Audio Out */
	for (aou_track = 0; aou_track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; aou_track ++)
	{
		d_global_track_nbr = player._track_info_list [GTK_TRACK_TYPE_AOU] [aou_track];
		const Player::TrackInfo &	aou_track_info =
			player._track_info [d_global_track_nbr];

		/* Repertorie toutes les pistes sources */
		for (track_index = 0; track_index < aou_track_info.mix.src.nbr_s_tracks; track_index ++)
		{
			track_type = aou_track_info.mix.src.track_conf [track_index].track_type;
			track_nbr  = aou_track_info.mix.src.track_conf [track_index].track_nbr;
			s_global_track_nbr = player._track_info_list [track_type] [track_nbr];

			/* Si la piste a deja ete mixee auparavant, inutile de s'attarder */
			if (track_mixed_to_aou_flag [track_index] [aou_track])
			{
				continue;
			}

			/* Si la piste n'a pas deja ete repertoriee */
			if (! MIX_sorted_track_flag [s_global_track_nbr])
			{
				/* Si c'est une piste d'effets, on
					va egalement chercher ses sources */
				if (track_type == Pattern_TYPE_FX)
				{
					MIX_explore_fx_track (track_nbr, s_global_track_nbr, track_mixed_to_aou_flag);
				}

				/* Sinon, on la place dans le tableau */
				else
				{
					MIX_sorted_track_flag [s_global_track_nbr] = true;
					MIX_use_of_track [s_global_track_nbr].first = MIX_total_nbr_tracks;
				}
			}

			MIX_track_order [MIX_total_nbr_tracks].s_track_type = track_type;
			MIX_track_order [MIX_total_nbr_tracks].s_track_nbr = track_nbr;
			MIX_track_order [MIX_total_nbr_tracks].s_track_index = track_index;
			MIX_track_order [MIX_total_nbr_tracks].d_track_type = GTK_TRACK_TYPE_AOU;
			MIX_track_order [MIX_total_nbr_tracks].d_track_nbr = aou_track;
			track_mixed_to_aou_flag [track_index] [aou_track] = true;

			MIX_use_of_track [s_global_track_nbr].last = MIX_total_nbr_tracks;
			MIX_total_nbr_tracks ++;
		}

		/* Si la piste courante est stereo, la piste suivante doit etre
			ignoree donc on la saute. */
		aou_track += aou_track_info.stereo - 1;
	}

/*______________________________________________
 *
 * Attribution des buffers intermediaires
 *______________________________________________
 */

	MIX_nbr_intermediate_buffers [0] = 0;
	MIX_nbr_intermediate_buffers [1] = 0;
	nbr_free_intermediate_buffers [0] = 0;
	nbr_free_intermediate_buffers [1] = 0;
	free_int_buf_ptr [0] = free_intermediate_buffers [0] + GTK_NBRTRACKS_MAXI * GTK_NBROUT_MAXI;
	free_int_buf_ptr [1] = free_intermediate_buffers [1] + GTK_NBRTRACKS_MAXI * GTK_NBROUT_MAXI;
	track_order_ptr = MIX_track_order;

	/* On parcourt chaque mixage */
	for (mix = 0; mix < MIX_total_nbr_tracks; mix ++)
	{
		s_global_track_nbr = player._track_info_list [track_order_ptr->s_track_type] [track_order_ptr->s_track_nbr];
		const Player::TrackInfo & s_track_info =
			player._track_info [s_global_track_nbr];

		d_global_track_nbr = player._track_info_list [track_order_ptr->d_track_type] [track_order_ptr->d_track_nbr];
		const Player::TrackInfo &	d_track_info =
			player._track_info [d_global_track_nbr];

		/* Piste source */
		used_buffer_type = 0;
		switch (track_order_ptr->s_track_type)
		{
		case	Pattern_TYPE_SPL:
			stereo = s_track_info.stereo - 1;
			break;
		case	Pattern_TYPE_FX:
			used_buffer_type = d_track_info.mix.src.track_conf [track_order_ptr->s_track_index].wet_flag ? 0 : 1;
			if (used_buffer_type == 1)
			{
				stereo = 1;	/* Le buffer dry est toujours stereo */
			}
			else
			{
				stereo = s_track_info.stereo - 1;
			}
			break;
		}

		/* Si la piste source est une Audio In, (ou que c'est une piste
			de samples utilisee qu'une seule fois directement pour la
			sortie Audio Out), pas besoin de buffer intermediaire. */
		if (   track_order_ptr->s_track_type == Pattern_TYPE_AIN
			 /*|| (   track_order_ptr->s_track_type == Pattern_TYPE_SPL
		        && track_order_ptr->d_track_type == GTK_TRACK_TYPE_AOU
		        &&    MIX_use_of_track [s_global_track_nbr].last
		           == MIX_use_of_track [s_global_track_nbr].first)*/)
		{
			track_order_ptr->s_buf_nbr = -1;
		}

		/* Sinon la piste est utilisee plusieurs fois. */
		else
		{
			/* Si c'est la premiere fois qu'on l'utilise, on cree un buffer.
			   Ca ne peut pas etre une piste FX donc on ne fait pas de buffer Wet. */
			if (mix == MIX_use_of_track [s_global_track_nbr].first)
			{
				/* S'il y a des buffers dans la pile des buffers deja utilises */
				if (nbr_free_intermediate_buffers [stereo] > 0)
				{
					nbr_free_intermediate_buffers [stereo] --;
					int_buf_for_global_track [s_global_track_nbr] [0] = *((free_int_buf_ptr [stereo]) ++);
				}
				/* Sinon on en prend un nouveau */
				else
				{
					int_buf_for_global_track [s_global_track_nbr] [0] = MIX_nbr_intermediate_buffers [stereo];
					MIX_nbr_intermediate_buffers [stereo] ++;
				}
			}

			track_order_ptr->s_buf_nbr = int_buf_for_global_track [s_global_track_nbr] [used_buffer_type];

			/* Si c'est la derniere fois qu'on utilise ce buffer,
				il faut le liberer. */
			if (mix == MIX_use_of_track [s_global_track_nbr].last)
			{
				*(--(free_int_buf_ptr [stereo])) = int_buf_for_global_track [s_global_track_nbr] [used_buffer_type];
				nbr_free_intermediate_buffers [stereo] ++;

				/* 2 buffers pour les effets (dry et wet) */
				if (track_order_ptr->s_track_type == Pattern_TYPE_FX)
				{
					if (used_buffer_type == 0)	// Libere aussi le dry
					{
						*(--(free_int_buf_ptr [1])) = int_buf_for_global_track [s_global_track_nbr] [1];
						nbr_free_intermediate_buffers [1] ++;
					}
					else								// Libere aussi le wet
					{
						*(--(free_int_buf_ptr [s_track_info.stereo - 1])) =
							int_buf_for_global_track [s_global_track_nbr] [0];
						nbr_free_intermediate_buffers [s_track_info.stereo - 1] ++;
					}
				}
			}
		}

		/* Piste destination */
		used_buffer_type = 0;
		switch (track_order_ptr->d_track_type)
		{
		case	Pattern_TYPE_FX:
			stereo = d_track_info.stereo - 1;
			used_buffer_type = 1;
			break;
		case	GTK_TRACK_TYPE_AOU:
			stereo = d_track_info.stereo - 1;
			break;
		}

		/* Si la piste destination est une Audio Out,
		   pas besoin de buffer intermediaire. */
		if (track_order_ptr->d_track_type == GTK_TRACK_TYPE_AOU)
		{
			track_order_ptr->d_buf_nbr = -1;
		}

		/* Sinon, il faut utiliser un buffer intermediaire. */
		else
		{
			/* Si c'est la premiere fois qu'on l'utilise, il faut la creer. */
			if (mix == MIX_use_of_track [d_global_track_nbr].first)
			{
				/* S'il y a des buffers dans la pile des buffers deja utilises */
				if (nbr_free_intermediate_buffers [stereo] > 0)
				{
					nbr_free_intermediate_buffers [stereo] --;
					int_buf_for_global_track [d_global_track_nbr] [0] = *((free_int_buf_ptr [stereo]) ++);
				}
				/* Sinon on en prend un nouveau */
				else
				{
					int_buf_for_global_track [d_global_track_nbr] [0] = MIX_nbr_intermediate_buffers [stereo];
					MIX_nbr_intermediate_buffers [stereo] ++;
				}

				/* Il y a un 2eme buffer pour les effets (dry, toujours stereo) */
				if (track_order_ptr->d_track_type == Pattern_TYPE_FX)
				{
					/* S'il y a des buffers dans la pile des buffers deja utilises */
					if (nbr_free_intermediate_buffers [1] > 0)
					{
						nbr_free_intermediate_buffers [1] --;
						int_buf_for_global_track [d_global_track_nbr] [1] = *((free_int_buf_ptr [1]) ++);
					}
					/* Sinon on en prend un nouveau */
					else
					{
						int_buf_for_global_track [d_global_track_nbr] [1] = MIX_nbr_intermediate_buffers [1];
						MIX_nbr_intermediate_buffers [1] ++;
					}
				}
			}

			track_order_ptr->d_buf_nbr = int_buf_for_global_track [d_global_track_nbr] [used_buffer_type];

			/* Si la piste destination est un effet et que l'index de la piste
			   source etait le dernier, on donne les numeros de buffers dry & wet
			   afin de pouvoir appliquer l'effet de l'un a l'autre quand le temps
			   sera venu (bref, quand on en sera au meme point, mais pendant le
			   mixage). */
			if (   track_order_ptr->d_track_type == Pattern_TYPE_FX
				 && track_order_ptr->s_track_index >= d_track_info.mix.src.nbr_s_tracks - 1)
			{
				track_order_ptr->wet_buf_nbr = int_buf_for_global_track [d_global_track_nbr] [0];
				track_order_ptr->dry_buf_nbr = int_buf_for_global_track [d_global_track_nbr] [1];
			}

			/* Si c'est la derniere fois que le buffer est utilise, on le
				met sur la pile des buffers libres. Ce cas peut arriver si
				une piste d'effet par exemple n'est pas redirigee vers une
				autre piste (AOU ou FX). */
			/*** Ce cas peut-il vraiment se produire ??? ***/
			if (mix == MIX_use_of_track [d_global_track_nbr].last)
			{
				*(--(free_int_buf_ptr [stereo])) = int_buf_for_global_track [d_global_track_nbr] [0];
				nbr_free_intermediate_buffers [stereo] ++;

				/* 2 buffers pour les effets (dry et wet) */
				if (track_order_ptr->s_track_type == Pattern_TYPE_FX)
				{
					*(--(free_int_buf_ptr [1])) = int_buf_for_global_track [d_global_track_nbr] [1];
					nbr_free_intermediate_buffers [1] ++;
				}
			}
		}

		track_order_ptr ++;
	}
}



/*==========================================================================*/
/*      Nom: MIX_explore_fx_track                                           */
/*      Description: Cherche recursivement les sources d'une piste d'effets */
/*                   pour les placer dans le tableau de mixage avant la     */
/*                   piste elle-meme.                                       */
/*      Parametres en entree:                                               */
/*        - track_nbr: numero de la piste d'effets.                         */
/*        - global_track_nbr: numero de piste generalise qui correspond a   */
/*                            track_nbr.                                    */
/*==========================================================================*/

void	MIX_explore_fx_track (int fx_track_nbr, int global_fx_track_nbr, bool track_mixed_to_aou_flag [GTK_NBRTRACKS_MAXI] [GTK_NBROUT_MAXI])
{
	int		track_type;
	int		track_nbr;
	int		track_type_2;
	int		track_nbr_2;
	int		track_index;
	int		track_index_2;
	int		global_track_nbr;
	int		global_track_nbr_2;
	int		aou_track;

	const Player &	player = Player::use_instance ();

	const Player::TrackInfo &	fx_track_info =
		player._track_info [global_fx_track_nbr];

	/* Repertorie toutes les pistes sources */
	for (track_index = 0; track_index < fx_track_info.mix.src.nbr_s_tracks; track_index ++)
	{
		track_type = fx_track_info.mix.src.track_conf [track_index].track_type;
		track_nbr  = fx_track_info.mix.src.track_conf [track_index].track_nbr;
		global_track_nbr = player._track_info_list [track_type] [track_nbr];

		/* Si la piste n'a pas deja ete repertoriee */
		if (! MIX_sorted_track_flag [global_track_nbr])
		{
			/* Si c'est une piste d'effets, on
				va egalement chercher ses sources */
			if (track_type == Pattern_TYPE_FX)
			{
				MIX_explore_fx_track (track_nbr, global_track_nbr, track_mixed_to_aou_flag);
			}

			/* Sinon, on la repertorie */
			else
			{
				MIX_sorted_track_flag [global_track_nbr] = true;
				MIX_use_of_track [global_track_nbr].first = MIX_total_nbr_tracks;
			}

			/* Si la piste source doit etre mixee a un buffer Audio Out, il
				faut la repertorier ici (si ce n'est pas encore fait). */
			for (aou_track = 0; aou_track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; aou_track ++)
			{
				global_track_nbr_2 = player._track_info_list [GTK_TRACK_TYPE_AOU] [aou_track];
				const Player::TrackInfo &	aou_track_info =
					player._track_info [global_track_nbr_2];

				for (track_index_2 = 0; track_index_2 < aou_track_info.mix.src.nbr_s_tracks; track_index_2 ++)
				{
					track_type_2 = aou_track_info.mix.src.track_conf [track_index_2].track_type;
					track_nbr_2  = aou_track_info.mix.src.track_conf [track_index_2].track_nbr;

					/* C'est la piste qu'on vient de faire ? */
					if (   track_type_2 == track_type
						 && track_nbr_2 == track_nbr)
					{
						MIX_track_order [MIX_total_nbr_tracks].s_track_type = track_type_2;
						MIX_track_order [MIX_total_nbr_tracks].s_track_nbr = track_nbr_2;
						MIX_track_order [MIX_total_nbr_tracks].s_track_index = track_index_2;
						MIX_track_order [MIX_total_nbr_tracks].d_track_type = GTK_TRACK_TYPE_AOU;
						MIX_track_order [MIX_total_nbr_tracks].d_track_nbr = aou_track;
						MIX_total_nbr_tracks ++;
						track_mixed_to_aou_flag [track_index_2] [aou_track] = true;
						break;
					}
				}

				/* Si la piste courante est stereo, la piste suivante doit etre
					ignoree donc on la saute. */
				aou_track += aou_track_info.stereo - 1;
			}
		}

		MIX_track_order [MIX_total_nbr_tracks].s_track_type = track_type;
		MIX_track_order [MIX_total_nbr_tracks].s_track_nbr = track_nbr;
		MIX_track_order [MIX_total_nbr_tracks].s_track_index = track_index;
		MIX_track_order [MIX_total_nbr_tracks].d_track_type = Pattern_TYPE_FX;
		MIX_track_order [MIX_total_nbr_tracks].d_track_nbr = fx_track_nbr;

		/* On marque la premiere utilisation de la
			piste d'effet sur la premiere track source. */
		if (track_index == 0)
		{
			MIX_sorted_track_flag [global_fx_track_nbr] = true;
			MIX_use_of_track [global_fx_track_nbr].first = MIX_total_nbr_tracks;
		}

		/* Si la piste source doit etre mixee a un buffer Audio Out, il
			faut la repertorier ici (si ce n'est pas encore fait). */
		for (aou_track = 0; aou_track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; aou_track ++)
		{
			global_track_nbr_2 = player._track_info_list [GTK_TRACK_TYPE_AOU] [aou_track];
			const Player::TrackInfo &	aou_track_info =
				player._track_info [global_track_nbr_2];

			for (track_index_2 = 0; track_index_2 < aou_track_info.mix.src.nbr_s_tracks; track_index_2 ++)
			{
				track_type_2 = aou_track_info.mix.src.track_conf [track_index_2].track_type;
				track_nbr_2  = aou_track_info.mix.src.track_conf [track_index_2].track_nbr;

				/* C'est la piste qu'on vient de faire ? */
				if (   track_type_2 == track_type
					 && track_nbr_2 == track_nbr)
				{
					/* La piste a deja ete faite dans ce buffer d'Audio Out,
						on peut passer au suivant. */
					if (track_mixed_to_aou_flag [track_index_2] [aou_track])
					{
						break;
					}

					/* La piste n'est pas encore mixee, on la met la. */
					else
					{
						MIX_total_nbr_tracks ++;
						MIX_track_order [MIX_total_nbr_tracks].s_track_type = track_type_2;
						MIX_track_order [MIX_total_nbr_tracks].s_track_nbr = track_nbr_2;
						MIX_track_order [MIX_total_nbr_tracks].s_track_index = track_index_2;
						MIX_track_order [MIX_total_nbr_tracks].d_track_type = GTK_TRACK_TYPE_AOU;
						MIX_track_order [MIX_total_nbr_tracks].d_track_nbr = aou_track;
						track_mixed_to_aou_flag [track_index_2] [aou_track] = true;
						break;
					}
				}
			}

			/* Si la piste courante est stereo, la piste suivante doit etre
				ignoree donc on la saute. */
			aou_track += aou_track_info.stereo - 1;
		}

		MIX_use_of_track [global_track_nbr].last = MIX_total_nbr_tracks;
		MIX_total_nbr_tracks ++;
	}
}



/*==========================================================================*/
/*      Nom: MIX_do_mix                                                     */
/*      Description: Routine principale de mixage. Doit etre appelee dans   */
/*                   la routine d'interruption du driver apres la routine   */
/*                   de gestion de partition.                               */
/*==========================================================================*/

void	MIX_do_mix ()
{
	int		s_track_type;
	int		d_track_type;
	int		stereo;
	int		s_stereo;
	int		d_stereo;
	int		buffer;
	int		track_index;
	int		mix_cnt;
	int		s_global_track_nbr;
	int		d_global_track_nbr;
	int		track;
	bool		wet_flag;
	bool		erase_flag;
	bool		mute_flag;
	float *	s_buffer_ptr;
	float *	d_buffer_ptr;
	MIX_TRACK_ORDER *	track_order_ptr;

	GTK_mutex.wait ();

	Player &			player = Player::use_instance ();

	/* Mise a jour du tableau de mixage des voies */
	MIX_create_mix_array ();

/*______________________________________________
 *
 * Recalcule les adresses de chaque buffer
 *______________________________________________
 */

	{
		float *	buffer_ptr = MIX_buffer_zone_ptr;
		LWORD	buffer_length = MIX_frame_length;		/* 24 bits -> 4 octets */

		for (stereo = 0; stereo < 2; stereo ++)
		{
			for (buffer = 0; buffer < MIX_nbr_intermediate_buffers [stereo]; buffer ++)
			{
				MIX_int_buffer_ptr [stereo] [buffer] = buffer_ptr;
				buffer_ptr += buffer_length;
			}
			buffer_length *= 2;		/* Passage en stereo */
		}
	}

	/* Parcourt les mixages un par un */
	track_order_ptr = MIX_track_order;
	for (mix_cnt = 0; mix_cnt < MIX_total_nbr_tracks; mix_cnt ++)
	{

/*______________________________________________
 *
 * Preparation au mixage
 *______________________________________________
 */

		track_index = track_order_ptr->s_track_index;
		erase_flag = (track_index == 0);
		s_track_type = track_order_ptr->s_track_type;
		d_track_type = track_order_ptr->d_track_type;

		/* Piste source */
		s_global_track_nbr =
			player._track_info_list [s_track_type] [track_order_ptr->s_track_nbr];
		Player::TrackInfo	&	s_track_info =
			player._track_info [s_global_track_nbr];

		/* Piste destination */
		d_global_track_nbr =
			player._track_info_list [d_track_type] [track_order_ptr->d_track_nbr];
		Player::TrackInfo	&	d_track_info =
			player._track_info [d_global_track_nbr];

		// Mix in a single pass or in two passes. Two passes is valid only with
		// sample tracks
		// Si la source est un buffer et pas le sample lui-meme
		const bool		two_pass_flag =
			(   s_track_type == Pattern_TYPE_SPL
			 && track_order_ptr->s_buf_nbr >= 0);

		// Finds the number of channels for the destination
		switch (d_track_type)
		{
		/* -> FX */
		case	Pattern_TYPE_FX:
			d_stereo = 2;
			break;

		/* -> Audio Out */
		case	GTK_TRACK_TYPE_AOU:
			d_stereo = d_track_info.stereo;
			break;

		default:
			assert (false);
			break;
		}

		// Calcule le volume de mixage et la balance selon les types des pistes,
		// ainsi que le flag de mute.
		// En mode deux passes, ces volumes ne concernent que la deuxieme passe,
		// (partie specifique a la destination), les volumes de la premiere etant
		// controles au niveau des voies.
		LWORD		mix_volume  = 0x1000;
		LWORD		mix_panning = 0x8000;
		mute_flag = s_track_info.wet_mute_flag;
		switch (s_track_type)
		{
		/* Sample -> */
		case	Pattern_TYPE_SPL:
			s_stereo = s_track_info.stereo;
			if (two_pass_flag)
			{
				// Mono -> Stereo: we set the panning on the second pass
				// because the first pass will keep the sample mono hence
				// losing the panning position.
				if (s_stereo < d_stereo)
				{
					mix_panning = s_track_info.outpan;
				}
			}
			else
			{
				mix_volume = s_track_info.outvol;
				mix_panning = s_track_info.outpan;
			}
			break;

		/* Audio In -> */
		case	Pattern_TYPE_AIN:
			mix_volume = s_track_info.outvol;
			mix_panning = s_track_info.outpan;
			s_stereo = s_track_info.stereo;
			break;

		/* FX -> */
		case	Pattern_TYPE_FX:
			wet_flag = d_track_info.mix.src.track_conf [track_index].wet_flag;
			s_stereo = s_track_info.stereo;
			
			/* FX (wet) -> */
			if (wet_flag)
			{
				mix_volume = s_track_info.outvol;
				mix_panning = s_track_info.outpan;
			}
			/* FX (dry) -> */
			else
			{
				mute_flag = s_track_info.dry_mute_flag;
				mix_volume = s_track_info.dry_outvol;
				mix_panning = s_track_info.dry_outpan;
				s_stereo = 2;
			}
			break;

		default:
			assert (false);
			break;
		}

		MIX_apply_dest_track_mix_param (
			mix_volume,
			mix_panning,
			d_track_info,
			d_track_type,
			track_index
		);

		/* En cas de source stereo, il faut doubler les volumes de mixage pour
		   conserver la quantite de samples avant et apres mixage. Par contre si
		   la source est un sample, on n'applique pas cette regle (moyenne des
		   cannaux). */
		if (s_track_type != Pattern_TYPE_SPL && s_stereo == 2)
		{
			mix_volume <<= 1;
		}

		/* Stockage pour le decliqueur */
		StereoGain &	cr_track = d_track_info.mix.src.track_conf [track_index].cr;
		cr_track.push_new_mix_param (mix_volume, mix_panning);

		/* Recupere l'adresse du buffer source */
		if (track_order_ptr->s_buf_nbr >= 0)
		{
			s_buffer_ptr = MIX_int_buffer_ptr [s_stereo - 1] [track_order_ptr->s_buf_nbr];
		}

		/* Adresse du buffer d'Audio In */
		else if (s_track_type == Pattern_TYPE_AIN)
		{
			s_buffer_ptr = MIX_driver_out_buffer_ptr [track_order_ptr->s_track_nbr];
		}

		/* Recupere l'adresse du buffer destination */
		if (track_order_ptr->d_buf_nbr >= 0)
		{
			d_buffer_ptr = MIX_int_buffer_ptr [d_stereo - 1] [track_order_ptr->d_buf_nbr];
		}

		/* Adresse du buffer Audio Out */
		else
		{
			d_buffer_ptr = MIX_driver_out_buffer_ptr [track_order_ptr->d_track_nbr];
		}

/*______________________________________________
 *
 * Mixage
 *______________________________________________
 */

		/* Si la piste source est un sample */
		if (s_track_type == Pattern_TYPE_SPL)
		{
#if 0

			float *			render_ptr = MIX_buffer_tmp_proc;
			bool				render_flag = true;
			/* Si la source est un buffer et pas le sample lui-meme */
			if (track_order_ptr->s_buf_nbr >= 0)
			{
				/* Rendu seulement si le sample est utilise pour la premiere fois */
				render_flag = (MIX_use_of_track [s_global_track_nbr].first >= mix_cnt);
				render_ptr = s_buffer_ptr;
			}

			if (render_flag)
			{
				// Quick fix:
				// we assume that a stereo audio buffer can fit on the stack.
				float				tmp_buf [MIX_MAX_FRAME_LENGTH * 2];

				// First ghost track is rendered on the real rendering buffer.
				// Next ones will be rendered on the temporary buffer, then mixed.
				float *			locdest_ptr = render_ptr;

				for (int ghost_cnt = 0; ghost_cnt < s_track_info.mix.spl.nbr_voices; ++ ghost_cnt)
				{
					const int		ghost_index =
						s_track_info.mix.spl.voice_index_arr [ghost_cnt];
					Voice &			voice = player._voice_arr [ghost_index];

					/*	Reechantillonne le sample dans le buffer source en
						tenant compte de la resolution du sample, et en
						laissant volume et stereo tels quels. */
					const bool		gen_flag = MIX_resample (
						s_track_info,
						locdest_ptr,
						s_stereo,
						ghost_index
					);

					/* Applique le filtre passe-bas si necessaire */
					if (voice.filter._active_flag)
					{
						FILT_do_iir_filtering (
							locdest_ptr,
							locdest_ptr,
							s_stereo,
							s_stereo,
							voice.filter.coef_x,
							voice.filter.coef_y,
							3,
							voice.filter.buffer_x,
							voice.filter.buffer_y,
							voice.filter.buffer_pos,
							Voice::Filter::BUF_LEN,
							MIX_frame_length
						);
					}

					// Possible fade out
					voice.process_post (locdest_ptr, MIX_frame_length, s_stereo);

					if (ghost_cnt == 0)
					{
						locdest_ptr = tmp_buf;
					}
					else if (gen_flag)
					{
						// Mixes the temporary buffer on the real rendering buffer.
						assert (locdest_ptr != render_ptr);
						MIX_add_buffer (
							render_ptr,
							locdest_ptr,
							MIX_frame_length,
							s_stereo
						);
					}
				}
			}

			/* Mixe le buffer source sur le buffer destination, en tenant
				compte de la stereo de chaque buffer. */
			MIX_mix_track (
				render_ptr,
				d_buffer_ptr,
				MIX_frame_length,
				s_stereo,
				d_stereo,
				cr_track,
				erase_flag
			);

#else

			float *			render_ptr    = d_buffer_ptr;
			int				render_stereo = d_stereo;
			bool				render_flag   = true;
			bool				empty_flag    = erase_flag;
			/* Si la source est un buffer et pas le sample lui-meme */
			if (track_order_ptr->s_buf_nbr >= 0)
			{
				/* Rendu seulement si le sample est utilise pour la premiere fois */
				render_flag   = (MIX_use_of_track [s_global_track_nbr].first >= mix_cnt);
				render_ptr    = s_buffer_ptr;
				render_stereo = s_stereo;
				empty_flag    = render_flag;
			}

			// Rendering
			if (render_flag)
			{
				for (int ghost_cnt = 0; ghost_cnt < s_track_info.mix.spl.nbr_voices; ++ ghost_cnt)
				{
					const int		ghost_index =
						s_track_info.mix.spl.voice_index_arr [ghost_cnt];
					Voice &			voice = player._voice_arr [ghost_index];

					LWORD				loc_vol = voice._outvol;
					LWORD				loc_pan = voice._outpan;
					if (! two_pass_flag)
					{
						MIX_apply_dest_track_mix_param (
							loc_vol,
							loc_pan,
							d_track_info,
							d_track_type,
							track_index
						);
					}
					voice._cr.push_new_mix_param (loc_vol, loc_pan);

					/*	Reechantillonne le sample dans le buffer source en
						tenant compte de la resolution du sample, et en
						laissant volume et stereo tels quels. */
					const bool		gen_flag = MIX_resample (
						s_track_info,
						MIX_buffer_tmp_proc,
						s_stereo,
						ghost_index
					);

					/* Applique le filtre passe-bas si necessaire */
					if (voice.filter._active_flag)
					{
						FILT_do_iir_filtering (
							MIX_buffer_tmp_proc,
							MIX_buffer_tmp_proc,
							s_stereo,
							s_stereo,
							voice.filter.coef_x,
							voice.filter.coef_y,
							3,
							voice.filter.buffer_x,
							voice.filter.buffer_y,
							voice.filter.buffer_pos,
							Voice::Filter::BUF_LEN,
							MIX_frame_length
						);
					}

					// Possible fade out
					voice.process_post (MIX_buffer_tmp_proc, MIX_frame_length, s_stereo);

					// Mixes the temporary buffer on the real rendering buffer.
					MIX_mix_track (
						MIX_buffer_tmp_proc,
						render_ptr,
						MIX_frame_length,
						s_stereo,
						render_stereo,
						voice._cr,
						empty_flag,
						gen_flag,
						true
					);

					if (gen_flag)
					{
						empty_flag = false;
					}
				}

				if (empty_flag && render_ptr == s_buffer_ptr)
				{
					memset (
						s_buffer_ptr,
						0,
						MIX_frame_length * s_stereo * sizeof (*s_buffer_ptr)
					);
				}
			}

			// Simple buffer mix
			if (track_order_ptr->s_buf_nbr >= 0)
			{
				if (empty_flag && erase_flag)
				{
					memset (
						d_buffer_ptr,
						0,
						MIX_frame_length * d_stereo * sizeof (*d_buffer_ptr)
					);
				}

				else if (! empty_flag)
				{
					MIX_mix_track (
						s_buffer_ptr,
						d_buffer_ptr,
						MIX_frame_length,
						s_stereo,
						d_stereo,
						cr_track,
						erase_flag,
						true,
						false
					);
				}
			}

#endif
		}

		/* La piste source n'est pas un sample */
		else
		{
			if (mute_flag)
			{
				if (erase_flag)
				{
					memset (
						d_buffer_ptr,
						0,
						MIX_frame_length * d_stereo * sizeof (*d_buffer_ptr)
					);
				}
			}

			else
			{
				/* Mixe le buffer source sur le buffer destination, en tenant
					compte de la stereo de chaque buffer. */
				MIX_mix_track (
					s_buffer_ptr,
					d_buffer_ptr,
					MIX_frame_length,
					s_stereo,
					d_stereo,
					cr_track,
					erase_flag,
					true,
					false
				);
			}
		}

		/* Dernier index de la piste source */
		if (track_index >= d_track_info.mix.src.nbr_s_tracks - 1)
		{
			/* La piste destination est un effet */
			if (d_track_type == Pattern_TYPE_FX)
			{
				/* Appliquer l'effet sur le buffer wet */
				MIXFX_do_fx (
					d_track_info,
				   MIX_int_buffer_ptr [d_stereo            - 1] [track_order_ptr->dry_buf_nbr],
				   MIX_int_buffer_ptr [d_track_info.stereo - 1] [track_order_ptr->wet_buf_nbr]
				);
			}
		}

		track_order_ptr ++;	/* Mixage suivant */
	}

	/* Fait avancer les samples */
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; track ++)
	{
		MIX_set_new_sample_position (track);
	}

	GTK_mutex.signal ();
}



// Increments some counters to keep things consistent when we just want to
// handle the score, not to mix anything.
void	MIX_do_not_mix ()
{
	Player &			player = Player::use_instance ();
	const int		nbr_tracks = GTK_nbr_tracks [Pattern_TYPE_SPL];
	for (int track_cnt = 0; track_cnt < nbr_tracks; ++track_cnt)
	{
		Player::TrackInfo &	track_info =
			player._track_info [player._track_info_list [Pattern_TYPE_SPL] [track_cnt]];

		for (int ghost_cnt = 0; ghost_cnt < track_info.mix.spl.nbr_voices; ++ ghost_cnt)
		{
			const int		ghost_index =
				track_info.mix.spl.voice_index_arr [ghost_cnt];
			Voice &			voice = player._voice_arr [ghost_index];
			if (voice.is_active ())
			{
				voice.process_post ();
			}
		}
	}
}



void	MIX_set_sample_group (int group)
{
	assert (group >= 0);

	MIX_sample_group = group;
}



int	MIX_get_sample_group ()
{
	return (MIX_sample_group);
}



void	MIX_apply_dest_track_mix_param (LWORD &mix_volume, LWORD &mix_panning, Player::TrackInfo &d_track_info, int d_track_type, int track_index)
{
	switch (d_track_type)
	{
	/* -> FX */
	case	Pattern_TYPE_FX:
		{
			mix_volume *= d_track_info.mix.src.track_conf [track_index].inpvol;
			mix_volume >>= 8;
			LWORD			temp = d_track_info.mix.src.track_conf [track_index].inppan;
			LWORD			temp_2 = temp;
			if (temp > 0x8000L)
			{
				temp = 0x10000L - temp;
			}
			mix_panning -= 0x8000L;
			mix_panning *= temp;
			mix_panning /= 0x8000L;
			mix_panning += temp_2;
		}
		break;

	/* -> Audio Out */
	case	GTK_TRACK_TYPE_AOU:
		mix_volume *= d_track_info.outvol;
		mix_volume >>= 8;
		break;

	default:
		assert (false);
		break;
	}
}



/*==========================================================================*/
/*      Nom: MIX_set_new_sample_position                                    */
/*      Description: Actualise la position d'un sample.                     */
/*      Parametres en entree:                                               */
/*        - track: numero de la piste de sample qu'on veut mettre a jour.   */
/*==========================================================================*/

void	MIX_set_new_sample_position (int track)
{
	Player &			player = Player::use_instance ();
	Player::TrackInfo &	track_info =
		player._track_info [player._track_info_list [Pattern_TYPE_SPL] [track]];

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

		const double	rel_freq =
			player.convert_period_to_rel_freq (voice._outperiod);
		const double	rate = rel_freq * voice.spl.freq / MIX_replay_freq;

		voice.spl.advance_cursor (MIX_frame_length, rate);
	}
}



/*==========================================================================*/
/*      Nom: MIX_resample                                                   */
/*      Description: Reechantillonne et mixe un sample quelconque dans un   */
/*                   buffer float mono ou stereo.                           */
/*      Parametres en entree:                                               */
/*        - d_stereo: nombre de voies du buffer destination (1 ou 2).       */
/*        - voice_index: voie fantome a mixer.                              */
/*      Parametres en entree/sortie:                                        */
/*        - track_info: piste de sample.                                    */
/*        - d_buffer_ptr: pointeur sur le buffer destination.               */
/*==========================================================================*/

bool	MIX_resample (Player::TrackInfo &track_info, float *d_buffer_ptr, int d_stereo, int voice_index)
{
	Player &			player = Player::use_instance ();
	Voice &			voice = player._voice_arr [voice_index];

	const long		nbr_spl = MIX_frame_length;

	bool				gen_flag = true;

	/* Piste mute ou sample termine: on met du blanc et on se tire derechef */
	if (   track_info.wet_mute_flag
	    || ! voice.is_active ()
		 || ! voice.is_compatible_with_group (MIX_sample_group))
	{
		memset (d_buffer_ptr, 0, nbr_spl * d_stereo * sizeof (*d_buffer_ptr));
		gen_flag = false;
	}

	else
	{
		voice.spl.process_block (
			d_buffer_ptr,
			nbr_spl,
			d_stereo,
			voice_index
		);
	}

	return (gen_flag);
}



/*==========================================================================*/
/*      Nom: MIX_mix_track                                                  */
/*      Description: Mixe un buffer sur un autre, avec modification         */
/*                   du volume. La stereo de chaque buffer est prise en     */
/*                   compte, ainsi que le mix/ecrasement. Les overflows ne  */
/*                   sont pas controles.                                    */
/*      Parametres en entree:                                               */
/*        - s_buffer_ptr: adresse du buffer source.                         */
/*        - length: longueur a mixer, en samples.                           */
/*        - s_stereo: nombre de voies du buffer source (1 ou 2).            */
/*        - d_stereo: nombre de voies du buffer destination (1 ou 2).       */
/*        - cr: etats et volumes de mixage (0...10000...?).                 */
/*          Le nouveau volume est fixe a cr.new_vol []                      */
/*        - erase_flag: true indique qu'on doit mettre directement les      */
/*                      donnees dans le buffer destination au lieu de les   */
/*                      mixer avec son ancien contenu.                      */
/*      Parametres en entree/sortie:                                        */
/*        - d_buffer_ptr: adresse du buffer destination.                    */
/*==========================================================================*/

void	MIX_mix_track (const float *s_buffer_ptr,
							float *d_buffer_ptr,
							LWORD length,
							int s_stereo,
							int d_stereo,
							StereoGain &cr,
							bool erase_flag,
		               bool proc_audio_flag,
							bool preserve_stereo_vol_flag)
{
	const float		vol_mult = 1.0f / (1L << 16);
	float				vol [2];
	for (int chn = 0; chn < 2; ++chn)
	{
		vol [chn] = cr.new_vol [chn] * vol_mult;

#if 0
		if (cr.old_vol [chn] == 0 && cr.new_vol [chn] > 0)
		{
			// Disable ramping when turning on the volume to preserve attacks.
			cr._gain [chn].force (vol [chn]);
		}
		else
		{
			cr._gain [chn].set (vol [chn]);
		}
#else
		cr._gain [chn].set (vol [chn]);
#endif
	};

	/* Test preliminaire */
	if (length <= 0)
	{
		return;
	}

	if (! proc_audio_flag)
	{
		cr._gain [0].skip (length);
		cr._gain [1].skip (length);
	}

	else
	{
		GainSmooth *	g_ptr [2] = { &cr._gain [0], &cr._gain [1] };
		GainSmooth		g_tmp [2] = {  cr._gain [0],  cr._gain [1] };
		if (s_stereo == 2 && preserve_stereo_vol_flag)
		{
			cr._gain [0].skip (length);
			cr._gain [1].skip (length);
			g_tmp [0].scale (2);
			g_tmp [1].scale (2);
			g_ptr [0] = &g_tmp [0];
			g_ptr [1] = &g_tmp [1];
		}

		switch ((s_stereo << 1) + d_stereo)
		{
		// Mono -> mono
		case	(1<<1) + 1:
			{
				GainSmooth		gain (cr._gain [0]);
				gain.add (cr._gain [1]);
				gain.mix_or_replace (d_buffer_ptr, s_buffer_ptr, length, 1, 1, erase_flag);
				cr._gain [0].skip (length);
				cr._gain [1].skip (length);
			}
			break;

		// Mono -> stereo
		case	(1<<1) + 2:
			cr._gain [0].mix_or_replace (d_buffer_ptr,     s_buffer_ptr,     length, 2, 1, erase_flag);
			cr._gain [1].mix_or_replace (d_buffer_ptr + 1, s_buffer_ptr,     length, 2, 1, erase_flag);
			break;

		// Stereo -> mono
		case	(2<<1) + 1:
			g_ptr [0]->mix_or_replace (d_buffer_ptr,     s_buffer_ptr,     length, 1, 2, erase_flag);
			g_ptr [1]->mix_or_replace (d_buffer_ptr,     s_buffer_ptr + 1, length, 1, 2, false);
			break;

		// Stereo -> stereo
		case	(2<<1) + 2:
			g_ptr [0]->mix_or_replace (d_buffer_ptr,     s_buffer_ptr,     length, 2, 2, erase_flag);
			g_ptr [1]->mix_or_replace (d_buffer_ptr + 1, s_buffer_ptr + 1, length, 2, 2, erase_flag);
			break;

		default:
			assert (false);
			break;
		}
	}
}



template <class T>
void	MIX_copy_buffer (float dest_ptr [], const float src_ptr [], long len, int stereo)
{
	assert (dest_ptr != 0);
	assert (src_ptr != 0);
	assert (src_ptr != dest_ptr);
	assert (len > 0);
	assert (stereo > 0);
	assert (stereo <= 2);

	len *= stereo;
	long				pos = 0;
	do
	{
		T::assign (dest_ptr [pos], src_ptr [pos]);
		++ pos;
	}
	while (pos < len);
}



template <class T>
void	MIX_copy_buffer (float dest_ptr [], const float src_ptr [], long len, int d_stereo, int s_stereo)
{
	assert (dest_ptr != 0);
	assert (src_ptr != 0);
	assert (src_ptr != dest_ptr);
	assert (len > 0);
	assert (d_stereo > 0);
	assert (d_stereo <= 2);
	assert (s_stereo > 0);
	assert (s_stereo <= 2);

	switch ((s_stereo << 1) + d_stereo)
	{
	// Mono -> mono
	// Stereo -> stereo
	case	(1<<1) + 1:
	case	(2<<1) + 2:
		MIX_copy_buffer <T> (dest_ptr, src_ptr, len, d_stereo);
		break;

	// Mono -> stereo
	case	(1<<1) + 2:
		for (long pos = 0; pos < len; ++pos)
		{
			const float		val = src_ptr [pos];
			T::assign (dest_ptr [pos * 2    ], val);
			T::assign (dest_ptr [pos * 2 + 1], val);
		}
		break;

	// Stereo -> mono
	case	(2<<1) + 1:
		for (long pos = 0; pos < len; ++pos)
		{
			T::assign (dest_ptr [pos], src_ptr [pos * 2] + src_ptr [pos * 2 + 1]);
		}
		break;

	default:
		assert (false);
		break;
	}
}



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

