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

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

#include	<dsound.h>

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"sdrv_dx.h"
#include	"SoundDriver.h"
#include	"splhandl.h"



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

/* Taille du buffer du text de debuggage */
#define	SDRVDX_MAX_REPORT_MSG_LEN	4095

/* Nombre maximum de buffers de mixage */
#define	SDRVDX_MAX_NBR_BUFFERS		4

/* Taille maximum d'un buffer de mixage, en samples */
#define	SDRVDX_MAX_BUFFER_LEN		1024



/*\\\ MACROS PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



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



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

signed int	SDRVDX_add_report_msg (const char *text_0);
signed int	SDRVDX_start_direct_sound (void);
signed int	SDRVDX_stop_direct_sound (void);
signed int	SDRVDX_set_format (void);
signed int	SDRVDX_play_buffer (void);
void	SDRVDX_reset_buffer_pos (long replay_pos);
signed int	SDRVDX_data_transfert (long frame_length, float *out_buffer_ptr [], float *in_buffer_ptr [], float *out_clip_ptr);



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



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

char	SDRVDX_report_msg_0 [SDRVDX_MAX_REPORT_MSG_LEN+1] = "";	// Message de la console

const long	SDRVDX_std_sample_freq [] =
{
	11025, 22050, 44100,
	0	// Indique la fin de la liste
};
bool	SDRVDX_direct_sound_started_flag = false;	// Indique que DirectSound est actif
bool	SDRVDX_valid_config_flag = false;			// Indique si une configuration valide a deja ete entree
bool	SDRVDX_player_started_flag = false;			// Indique que le player joue (meme s'il est deconnecte)
bool	SDRVDX_try_to_connect_flag = false;			// Indique qu'on reessayera de se connecter car le periph etait occupe.
SoundDriver_CONFIG	SDRVDX_current_config;		// Configuration courante
int	SDRVDX_out_stereo_tab [2];						// Config stereo de chaque canal
int	SDRVDX_in_stereo_tab [2];
long	SDRVDX_current_frame_length = SDRVDX_MAX_BUFFER_LEN/2;
long	SDRVDX_buffer_pos_byte [SDRVDX_MAX_NBR_BUFFERS+1];	// Positions des buffers dans le buffer circulaire, en octets. 0 = le plus recent
																// On a besoin de la position d'un buffer de plus pour calculer le temps d'attente avant l'envoi des donnees.

HWND	SDRVDX_window_handle = NULL;
LPDIRECTSOUND	SDRVDX_ds_object_ptr = NULL;		// Pointeur sur l'objet DirectSound
bool	SDRVDX_priority_level_flag = false;			// Indique si on a le controle sur DirectSound
LPDIRECTSOUNDBUFFER	SDRVDX_primary_buffer_ptr = NULL;
LPDIRECTSOUNDBUFFER	SDRVDX_mix_buffer_ptr = NULL;
long	SDRVDX_mix_buffer_length_bytes;
int	SDRVDX_latency_time = 350;						// Temps de latence estime, en ms



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



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*        - param_ptr: handle de la fenetre associee a l'application qui    */
/*                     utilise le driver, NULL si aucune fenetre.           */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/

signed int cdecl	SDRVDX_init (void *param_ptr)
{
	OSVERSIONINFOW	windows_version;

	SDRVDX_window_handle = (HWND) param_ptr;
	SDRVDX_valid_config_flag = false;
	SDRVDX_player_started_flag = false;
	SDRVDX_try_to_connect_flag = false;

	/* On adapte le temps de latence en fonction de la version
		de Windows sur laquelle on tourne. */
	windows_version.dwOSVersionInfoSize = sizeof (windows_version);
	if (FAILED (::GetVersionExW (&windows_version)))
	{
		SDRVDX_add_report_msg ("SDRVDX_init: Error: couldn't get Windows version.\n");
		return (-1);
	}

	if (   windows_version.dwPlatformId == VER_PLATFORM_WIN32_NT
       && windows_version.dwMajorVersion == 4)
	{
		SDRVDX_latency_time = 350;
	}
	else
	{
		SDRVDX_latency_time = 50;
	}

	return (0);
}



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

signed int cdecl	SDRVDX_restore (void)
{
	if (SDRVDX_player_started_flag)
	{
		SDRVDX_stop_replay ();
	}

	SDRVDX_valid_config_flag = false;

	if (SDRVDX_stop_direct_sound ())
	{
		return (-1);
	}

	return (0);
}



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

signed int cdecl	SDRVDX_set_config (SoundDriver_CONFIG *config_ptr)
{
	int		freq_cnt;
	int		chn_cnt;

	/* Verifie qu'on n'appelle pas le changement de config pendant qu'on joue */
	if (SDRVDX_player_started_flag)
	{
		SDRVDX_add_report_msg ("SDRVDX_set_config: Error: SDRVDX_set_config called while playing.\n");
		return (-1);
	}
	
	/* Valide les parametres */
	config_ptr->nbr_buffers = MIN (config_ptr->nbr_buffers, SDRVDX_MAX_NBR_BUFFERS);
	config_ptr->nbr_buffers = MAX (config_ptr->nbr_buffers, 2);
	config_ptr->nbr_out = 2;	// 2 pistes en stereo: on impose le schema standard
	config_ptr->nbr_in = 2;
	config_ptr->out_stereo_ptr [0] = 2;
	config_ptr->out_stereo_ptr [1] = 0;
	config_ptr->in_stereo_ptr [0] = 2;
	config_ptr->in_stereo_ptr [1] = 0;

	/* La frequence de replay est fixee a la frequence standard
		immediatement au-dessus. */
	freq_cnt = 0;
	do
	{
		if (config_ptr->sample_freq < SDRVDX_std_sample_freq [freq_cnt])
		{
			config_ptr->sample_freq = SDRVDX_std_sample_freq [freq_cnt];
			break;
		}
		freq_cnt ++;
	}
	while (SDRVDX_std_sample_freq [freq_cnt] > 0);
	if (config_ptr->sample_freq > SDRVDX_std_sample_freq [freq_cnt - 1])
	{
		config_ptr->sample_freq = SDRVDX_std_sample_freq [freq_cnt - 1];
	}

	/* Enregistre la configuration */
	SDRVDX_current_config = *config_ptr;
	SDRVDX_current_config.out_stereo_ptr = SDRVDX_out_stereo_tab;
	SDRVDX_current_config.in_stereo_ptr = SDRVDX_in_stereo_tab;
	for (chn_cnt = 0; chn_cnt < config_ptr->nbr_out; chn_cnt ++)
	{
		SDRVDX_out_stereo_tab [chn_cnt] = config_ptr->out_stereo_ptr [chn_cnt];
	}
	for (chn_cnt = 0; chn_cnt < config_ptr->nbr_in; chn_cnt ++)
	{
		SDRVDX_in_stereo_tab [chn_cnt] = config_ptr->in_stereo_ptr [chn_cnt];
	}

	/* Calcule la taille du buffer de mixage */
	SDRVDX_mix_buffer_length_bytes =   MAX (  (long)SDRVDX_current_config.nbr_buffers
	                                        * SDRVDX_MAX_BUFFER_LEN,
	                                        SDRVDX_current_config.sample_freq)	// 1 seconde au moins
	                                 * (SDRVDX_current_config.nbr_out * 2);		// 16 bits

	/* Si DirectSound est initialise, on lui applique la nouvelle config. */
	if (SDRVDX_direct_sound_started_flag)
	{
		if (SDRVDX_set_format ())
		{
			SDRVDX_add_report_msg ("SDRVDX_set_config: Error: couldn't apply new configuration.\n");
			return (-1);
		}
	}

	SDRVDX_valid_config_flag = true;

	return (0);
}



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

signed int cdecl	SDRVDX_get_config (SoundDriver_CONFIG *config_ptr)
{
	if (! SDRVDX_valid_config_flag)
	{
		SDRVDX_add_report_msg ("SDRVDX_get_config: Error: sound isn't configured yet.\n");
		return (-1);
	}

	*config_ptr = SDRVDX_current_config;

	return (0);
}



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

signed int cdecl	SDRVDX_start_replay (void)
{
	if (! SDRVDX_valid_config_flag)
	{
		SDRVDX_add_report_msg ("SDRVDX_start_replay: Error: Player must be configured before started.\n");
		return (-1);
	}

	if (SDRVDX_player_started_flag)
	{
		return (0);
	}

	if (SDRVDX_is_connected ())
	{
		if (SDRVDX_play_buffer ())
		{
			SDRVDX_add_report_msg ("SDRVDX_start_replay: Error: cound't play buffer.\n");
			return (-1);
		}
	}
	else
	{
		SDRVDX_reset_buffer_pos (0);
	}

	SDRVDX_player_started_flag = true;

	return (0);
}



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

signed int cdecl	SDRVDX_stop_replay (void)
{
	if (SDRVDX_is_connected ())
	{
		SDRVDX_mix_buffer_ptr->Stop ();
	}

	SDRVDX_player_started_flag = false;

	return (0);
}



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

signed int cdecl	SDRVDX_set_new_frame (long frame_length)
{
	DWORD		current_read_pos_bytes;
	DWORD		current_write_pos_bytes;
	int		buf_cnt;
	long		frame_length_byte;
	long		distance;
	long		tick_length_ms;
	int		latency_time;

	if (frame_length > SDRVDX_MAX_BUFFER_LEN)
	{
		SDRVDX_add_report_msg ("SDRVDX_set_new_frame: Error: Frame too long.\n");
		return (-1);
	}

	if (! SDRVDX_player_started_flag)
	{
		SDRVDX_add_report_msg ("SDRVDX_set_new_frame: Error: Player not started.\n");
		return (-1);
	}

	/* Si on n'avait pas pu se connecter car le periph etait occupe, on reessaie */
	if (SDRVDX_try_to_connect_flag)
	{
		if (SDRVDX_connect ())
		{
			SDRVDX_add_report_msg ("SDRVDX_set_new_frame: Error: Failed to restore sound system.\n");
			return (-1);
		}
	}

	/* Fait avancer les buffers */
	frame_length_byte = SDRVDX_current_frame_length * ((16 >> 3) * 2);	// 16 bits stereo
	for (buf_cnt = SDRVDX_current_config.nbr_buffers; buf_cnt > 0; buf_cnt --)
	{
		SDRVDX_buffer_pos_byte [buf_cnt] = SDRVDX_buffer_pos_byte [buf_cnt - 1];
	}
	SDRVDX_buffer_pos_byte [0] += frame_length_byte;
	if (SDRVDX_buffer_pos_byte [0] >= SDRVDX_mix_buffer_length_bytes)
	{
		SDRVDX_buffer_pos_byte [0] -= SDRVDX_mix_buffer_length_bytes;
	}

	frame_length_byte = frame_length * ((16 >> 3) * 2);	// 16 bits stereo

	/* Mode connecte: recupere la position courante */
	if (SDRVDX_is_connected ())
	{
		while (FAILED (SDRVDX_mix_buffer_ptr->GetCurrentPosition (&current_read_pos_bytes,
																					 &current_write_pos_bytes)))
		{
			Sleep (10);
		}
		latency_time = SDRVDX_latency_time;
	}

	/* Mode deconnecte */
	else
	{
		current_read_pos_bytes = SDRVDX_buffer_pos_byte [SDRVDX_current_config.nbr_buffers];
		latency_time = 0;
	}

	/* Attend qu'on soit bien positionne */
	distance = BASE_get_distance_in_circular_buffer (SDRVDX_mix_buffer_length_bytes,
		current_read_pos_bytes, SDRVDX_buffer_pos_byte [SDRVDX_current_config.nbr_buffers - 1]);
	tick_length_ms =   (distance * 1000)
	                 / (SDRVDX_current_config.sample_freq * ((16 >> 3) * 2));
	tick_length_ms -= latency_time;
	tick_length_ms = MAX (tick_length_ms, 0);
	Sleep (tick_length_ms);

	/* Enregistre la duree de la frame */
	SDRVDX_current_frame_length = frame_length;

	return (0);
}



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

signed int cdecl	SDRVDX_set_data (float *out_buffer_ptr [], float *out_clip_ptr)
{
	void *	dest_1_ptr;
	void *	dest_2_ptr;
	DWORD		length_1;
	DWORD		length_2;
	float *	source_1_ptr;
	float *	source_2_ptr;
	float		clip_level;

	if (! SDRVDX_player_started_flag)
	{
		SDRVDX_add_report_msg ("SDRVDX_set_data: Error: Player not started.\n");
		return (-1);
	}

	/* Pas connecte: on s'en va directement */
	if (! SDRVDX_is_connected ())
	{
		return (0);
	}

	/* Demande l'adresse de notre buffer dans le buffer de DirectSound */
	if (FAILED (SDRVDX_mix_buffer_ptr->Lock (SDRVDX_buffer_pos_byte [0],
	                                         SDRVDX_current_frame_length * ((16 >> 3) * 2),	// 16 bits stereo
	                                         &dest_1_ptr, &length_1,
	                                         &dest_2_ptr, &length_2, 0)))
	{
		SDRVDX_add_report_msg ("SDRVDX_set_data: Error: Couldn't lock Output Buffer.\n");
		return (-1);
	}

	source_1_ptr = out_buffer_ptr [0];

	/* 1 piste stereo */
	if (SDRVDX_current_config.out_stereo_ptr [0] == 2)
	{
		/* 24 bits stereo -> 16 bits stereo */
		out_clip_ptr [0] = SPLH_copy_mem_flt_stereo_2_16_stereo
			((SWORD *) dest_1_ptr, source_1_ptr, length_1 >> 2);
		if (dest_2_ptr != NULL)
		{
			source_1_ptr += length_1 >> 1;
			clip_level = SPLH_copy_mem_flt_stereo_2_16_stereo
				((SWORD *) dest_2_ptr, source_1_ptr, length_2 >> 2);
			out_clip_ptr [0] = MAX (clip_level, out_clip_ptr [0]);
		}
	}

	/* 2 pistes mono */
	else
	{
		source_2_ptr = out_buffer_ptr [1];

		/* 2x24 bits mono -> 16 bits stereo */
		out_clip_ptr [0] = SPLH_copy_mem_flt_bimono_2_16_stereo
			((SWORD *) dest_1_ptr, source_1_ptr, source_2_ptr, length_1 >> 2);

		if (dest_2_ptr != NULL)
		{
			source_1_ptr += length_1 >> 2;
			source_2_ptr += length_1 >> 2;
			clip_level = SPLH_copy_mem_flt_bimono_2_16_stereo
				((SWORD *) dest_2_ptr, source_1_ptr, source_2_ptr, length_2 >> 2);
			out_clip_ptr [0] = MAX (clip_level, out_clip_ptr [0]);
		}
		out_clip_ptr [1] = out_clip_ptr [0];	// On met les memes mais c'est pas grave
	}

	SDRVDX_mix_buffer_ptr->Unlock (dest_1_ptr, length_1, dest_2_ptr, length_2);

	return (0);
}



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

signed int cdecl	SDRVDX_get_data (float *in_buffer_ptr [])
{
	if (! SDRVDX_player_started_flag)
	{
		SDRVDX_add_report_msg ("SDRVDX_get_data: Error: Player not started.\n");
		return (-1);
	}

	return (0);
}



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

signed int cdecl	SDRVDX_is_playing (void)
{
	return ((signed int) SDRVDX_player_started_flag);
}



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

signed int cdecl	SDRVDX_disconnect (void)
{
	SDRVDX_try_to_connect_flag = false;

	if (! SDRVDX_is_connected ())
	{
		return (0);
	}

	SDRVDX_stop_direct_sound ();

	return (0);
}



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

signed int cdecl	SDRVDX_connect (void)
{
	signed int	ret_val;

	if (SDRVDX_is_connected ())
	{
		return (0);
	}

	if (! SDRVDX_direct_sound_started_flag)
	{
		ret_val = SDRVDX_start_direct_sound ();
		if (ret_val == 1)
		{
			SDRVDX_try_to_connect_flag = true;
			return (0);
		}
		else if (ret_val < 0)
		{
			SDRVDX_add_report_msg ("SDRVDX_connect: Error: couldn't initialize DirectSound.\n");
			SDRVDX_try_to_connect_flag = false;	// On lui demande d'arreter d'essayer de se connecter sinon il va nous embeter a chaque frame
			return (-1);
		}
	}
	SDRVDX_try_to_connect_flag = false;

	if (SDRVDX_valid_config_flag)
	{
		if (SDRVDX_set_format ())
		{
			SDRVDX_add_report_msg ("SDRVDX_connect: Error: couldn't apply configuration.\n");
			return (-1);
		}
	}

	/* Demarre le replay des buffers */
	if (SDRVDX_player_started_flag)
	{
		if (SDRVDX_play_buffer ())
		{
			SDRVDX_add_report_msg ("SDRVDX_connect: Error: cound't play buffer.\n");
			return (-1);
		}
	}

	return (0);
}



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

signed int cdecl	SDRVDX_is_connected (void)
{
	return ((signed int) SDRVDX_direct_sound_started_flag);
}



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

signed int cdecl	SDRVDX_get_message (char *text_0, long max_len)
{
	const long		text_len = long (strlen (SDRVDX_report_msg_0));
	if (text_len == 0)
	{
		return (0);
	}

	if (max_len > 0)
	{
		if (text_len > max_len)
		{
			memcpy (text_0, SDRVDX_report_msg_0, max_len);
			text_0 [max_len] = '\0';
			memmove (SDRVDX_report_msg_0, SDRVDX_report_msg_0 + max_len,
			         text_len - max_len + 1);
		}
		else
		{
			memcpy (text_0, SDRVDX_report_msg_0, text_len + 1);
			SDRVDX_report_msg_0 [0] = '\0';
		}
	}

	else if (max_len == 0)
	{
		text_0 [0] = '\0';
	}

	return (1);
}



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

signed int cdecl	SDRVDX_get_latency (void)
{
	return (SDRVDX_latency_time);
}



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

signed int cdecl	SDRVDX_set_latency (int t)
{
	if (t <= 0)
	{
		SDRVDX_add_report_msg ("SDRVDX_set_latency: Error: bad time.\n");
		return (-1);
	}

	SDRVDX_latency_time = t;

	return (0);
}



/****************************************************************************/
/*                                                                          */
/*      PRIVATE FUNCTIONS                                                   */
/*                                                                          */
/****************************************************************************/



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

signed int	SDRVDX_add_report_msg (const char *text_0)
{
	int				ret_val = -1;
	const long		text_len = long (strlen (SDRVDX_report_msg_0));
	const long		new_text_len = long (strlen (text_0));
	if (text_len + new_text_len <= SDRVDX_MAX_REPORT_MSG_LEN)
	{
		strcat (SDRVDX_report_msg_0, text_0);
		ret_val = 0;
	}
	else if (text_len < SDRVDX_MAX_REPORT_MSG_LEN)
	{
		memcpy (SDRVDX_report_msg_0 + text_len, text_0,
		        SDRVDX_MAX_REPORT_MSG_LEN - text_len);
		SDRVDX_report_msg_0 [SDRVDX_MAX_REPORT_MSG_LEN] = '\0';
	}

	return (ret_val);
}



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 si le peripherique etait occupe,                          */
/*              -1 si erreur.                                               */
/*==========================================================================*/

signed int	SDRVDX_start_direct_sound (void)
{
	::DSBUFFERDESC	primary_buffer_desc;
	::HRESULT	ret_val;

	/* Creation de l'objet DirectSound */
	ret_val = ::DirectSoundCreate (NULL, &SDRVDX_ds_object_ptr, NULL);
	if (ret_val == DSERR_ALLOCATED)
	{
		return (1);	// Peripherique occupe: on reviendra plus tard
	}
	else if (FAILED (ret_val))
	{
		SDRVDX_ds_object_ptr = NULL;
		SDRVDX_add_report_msg ("SDRVDX_start_direct_sound: Error: couldn't create DirectSound object.\n");
		return (-1);
	}

	/* Changement du niveau de priorite */
	SDRVDX_priority_level_flag = false;
	if (SDRVDX_window_handle != NULL)
	{
		if (SUCCEEDED (SDRVDX_ds_object_ptr->SetCooperativeLevel (SDRVDX_window_handle,
		                                                          DSSCL_PRIORITY)))
		{
			SDRVDX_priority_level_flag = true;
		}
	}

	/* Creation du buffer primaire */
	memset (&primary_buffer_desc, 0, sizeof (primary_buffer_desc));
	primary_buffer_desc.dwSize = sizeof (primary_buffer_desc);
	primary_buffer_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
	primary_buffer_desc.dwBufferBytes = 0;
	primary_buffer_desc.lpwfxFormat = NULL;
	if (FAILED (SDRVDX_ds_object_ptr->CreateSoundBuffer (&primary_buffer_desc,
	                                                     &SDRVDX_primary_buffer_ptr,
	                                                     NULL)))
	{
		SDRVDX_add_report_msg ("SDRVDX_start_direct_sound: Error: couldn't create primary buffer.\n");
		return (-1);
	}

	SDRVDX_direct_sound_started_flag = true;

	return (0);
}



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

signed int	SDRVDX_stop_direct_sound (void)
{
	SDRVDX_direct_sound_started_flag = false;

	if (SDRVDX_ds_object_ptr != NULL)
	{
		SDRVDX_ds_object_ptr->Release ();
		SDRVDX_ds_object_ptr = NULL;
		SDRVDX_primary_buffer_ptr = NULL;
		SDRVDX_mix_buffer_ptr = NULL;
	}

	return (0);
}



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

signed int	SDRVDX_set_format (void)
{
	WAVEFORMATEX	buffer_attr;
	DSBUFFERDESC	mix_buffer_desc;

	/* Caracteristique du buffer primaire et de mixage */
	buffer_attr.wFormatTag = WAVE_FORMAT_PCM;
	buffer_attr.nChannels = 2;
	buffer_attr.wBitsPerSample = 16;
	buffer_attr.nSamplesPerSec = SDRVDX_current_config.sample_freq;
	buffer_attr.nBlockAlign = (buffer_attr.nChannels * buffer_attr.wBitsPerSample) >> 3;
	buffer_attr.nAvgBytesPerSec = buffer_attr.nSamplesPerSec * buffer_attr.nBlockAlign;
	buffer_attr.cbSize = 0;

	/* Changement du format du buffer primaire */
	if (SDRVDX_priority_level_flag)
	{
		if (FAILED (SDRVDX_primary_buffer_ptr->SetFormat (&buffer_attr)))
		{
			SDRVDX_add_report_msg ("SDRVDX_set_format: Warning: couldn't set primary buffer format.\n");
		}
	}

	/* Creation du buffer de mixage utilise par DirectSound */
	memset (&mix_buffer_desc, 0, sizeof (mix_buffer_desc));
	mix_buffer_desc.dwSize = sizeof (mix_buffer_desc);
	mix_buffer_desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_STICKYFOCUS;
	mix_buffer_desc.dwBufferBytes = SDRVDX_mix_buffer_length_bytes;
	mix_buffer_desc.lpwfxFormat = &buffer_attr;
	if (FAILED (SDRVDX_ds_object_ptr->CreateSoundBuffer (&mix_buffer_desc,
	                                                     &SDRVDX_mix_buffer_ptr,
																		  NULL)))
	{
		SDRVDX_add_report_msg ("SDRVDX_set_format: Error: couldn't create mix buffer.\n");
		return (-1);
	}

	return (0);
}



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

signed int	SDRVDX_play_buffer (void)
{
	DWORD		read_pos_bytes;
	DWORD		write_pos_bytes;

	if (FAILED (SDRVDX_mix_buffer_ptr->Play (0, 0, DSBPLAY_LOOPING)))
	{
		SDRVDX_add_report_msg ("SDRVDX_play_buffer: Error: couldn't start playing buffer.\n");
		return (-1);
	}

	if (FAILED (SDRVDX_mix_buffer_ptr->GetCurrentPosition (&read_pos_bytes, &write_pos_bytes)))
	{
		SDRVDX_add_report_msg ("SDRVDX_play_buffer: Error: couldn't get playing position.\n");
		return (-1);
	}

	SDRVDX_reset_buffer_pos (read_pos_bytes);

	return (0);
}



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

void	SDRVDX_reset_buffer_pos (long replay_pos)
{
	int		buf_cnt;

	for (buf_cnt = 0; buf_cnt < SDRVDX_current_config.nbr_buffers + 1; buf_cnt ++)
	{
		SDRVDX_buffer_pos_byte [buf_cnt] = replay_pos;
		replay_pos += (SDRVDX_MAX_BUFFER_LEN/2) * ((16 >> 3) * 2);	// 16 bits, stereo
		if (replay_pos >= SDRVDX_mix_buffer_length_bytes)
		{
			replay_pos -= SDRVDX_mix_buffer_length_bytes;
		}
	}
}



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



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

