#include <stdio.h>
#include "ysound.h"


void YiffMixBuffers(
	Audio *audio,
	SoundBuffer *tar_buf, const SoundBuffer *src_buf,
	YDataLength len,
	Coefficient volume_left, Coefficient volume_right
);

void YiffShortenBuffer16(
	u_int16_t *buf,
	int buf_len,		/* In u_int8_t (not u_int16_t) units. */
	int buf_shoren_len
);
void YiffShortenBuffer8(
	u_int8_t *buf,
	int buf_len,		/* In u_int8_t units. */
	int buf_shoren_len
);


#define MIN(a,b)        ((a) < (b) ? (a) : (b))
#define MAX(a,b)        ((a) > (b) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define ABSOLUTE(x)     (((x) < 0) ? ((x) * -1) : (x))


/*
 *	Mixes the source buffer to the target buffer and
 *	compensates for volume.
 */
void YiffMixBuffers(
	Audio *audio,
	SoundBuffer *tar_buf, const SoundBuffer *src_buf,
	YDataLength len,
	Coefficient volume_left,
	Coefficient volume_right
) 
{
	int i, b, blocks;
	SoundBuffer *tp;
	const SoundBuffer *sp;
	Coefficient lc, rc;
	Boolean flip;

   
	if((audio == NULL) ||
	   (tar_buf == NULL) ||
	   (src_buf == NULL) ||
	   (len <= 0)
	)
	    return;

	flip = audio->flip_stereo;

	/* Calculate coefficients. */
	if(audio->channels == 2)
	{
	    /* Stereo. */
	    blocks = 2;

	    /* Calculate left and right coefficients. */
	    lc = MAX(MIN(volume_left, 1), 0);
	    rc = MAX(MIN(volume_right, 1), 0);
	}
	else
	{
	    /* Mono. */
	    blocks = 1;

	    /* Average left and right coefficients. */
	    lc = (volume_left + volume_right) / 2;

	    rc = MAX(MIN(lc, 1), 0);
	    lc = rc;
	}

	/* Begin buffer mixing. */
	for(i = 0, b = 0, tp = tar_buf, sp = src_buf;
	    i < len;
	    i++, tp++, sp++
	) 
	{
	    /* Is source byte positive or negative? */
	    if(*sp < 0)
	    {
		/* Source byte is negative. */

		if(b == 0)
		    (*tp) = MAX((int)(*tp) +
			((int)(*sp) * ((flip) ? rc : lc)),
			(int)-128
		    );
		else
		    (*tp) = MAX((int)(*tp) +
			((int)(*sp) * ((flip) ? lc : rc)),
			(int)-128
		    );
	    }
	    else
	    {
		/* Source byte is positive. */

		if(b == 0)
		    (*tp) = MIN((int)(*tp) +
			((int)(*sp) * ((flip) ? rc : lc)),
			(int)127
		    );
		else
		    (*tp) = MIN((int)(*tp) +
			((int)(*sp) * ((flip) ? lc : rc)),
			(int)127
		    );
	    }

	    /* Increment block position b. */
	    b++;
	    if(b >= blocks)
		b = 0;
	}
}

/*
 *	Moves the samples (shortens) from the given buffer length
 *	to the shorten length.
 *
 *	Inputs assumed valid (not NULL and greater than 0).
 *
 *	Make sure the buffer is even number when allocated!
 */
void YiffShortenBuffer16(
	u_int16_t *buf,
	int buf_len,	/* In u_int8_t (not u_int16_t) units. */
	int buf_shoren_len
)
{
	u_int16_t	*tar_ptr = buf,
			*src_ptr = buf,
			*tar_end = buf + (buf_shoren_len / 2);

	int	src_pos = 0,	/* Pos and inc in units of * 256. */
		src_inc = 256 * buf_len / buf_shoren_len;

	/* Target length is assumed to be shorter than source len. */
	while(tar_ptr < tar_end)
	{
	    *tar_ptr++ = src_ptr[(src_pos >> 8)];
	    src_pos += src_inc;
	}                
}

/*
 *      Moves the samples (shortens) from the given buffer length
 *      to the shorten length.
 *
 *      Inputs assumed valid (not NULL and greater than 0).
 */
void YiffShortenBuffer8(
	u_int8_t *buf,
	int buf_len,	/* In u_int8_t units. */
	int buf_shoren_len
)
{
	u_int8_t	*tar_ptr = buf,
			*src_ptr = buf,
			*tar_end = buf + buf_shoren_len;

	int	src_pos = 0,	/* Pos and inc in units of * 256. */
		src_inc = 256 * buf_len / buf_shoren_len;

	/* Target length is assumed to be shorter than source len. */
	while(tar_ptr < tar_end)
	{
	    *tar_ptr++ = src_ptr[(src_pos >> 8)];
	    src_pos += src_inc;
	}
}
