/*
 *
 *  downmix.c
 *    
 *	Copyright (C) Aaron Holtzman - Sept 1999
 *
 *	Originally based on code by Yuqing Deng.
 *
 *  This file is part of ac3dec, a free Dolby AC-3 stream decoder.
 *	
 *  ac3dec 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, or (at your option)
 *  any later version.
 *   
 *  ac3dec 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "ac3.h"
#include "ac3_internal.h"


#include "decode.h"
#include "downmix.h"
#include "debug.h"


//Pre-scaled downmix coefficients
static float cmixlev_lut[4] = { 0.2928, 0.2468, 0.2071, 0.2468 };
static float smixlev_lut[4] = { 0.2928, 0.2071, 0.0   , 0.2071 };

static void 
downmix_3f_2r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	uint_32 j;
	float right_tmp;
	float left_tmp;
	float clev,slev;
	float *centre = 0, *left = 0, *right = 0, *left_sur = 0, *right_sur = 0;

	left      = samples[0];
	centre    = samples[1];
	right     = samples[2];
	left_sur  = samples[3];
	right_sur = samples[4];

	clev = cmixlev_lut[bsi->cmixlev];
	slev = smixlev_lut[bsi->surmixlev];

	for (j = 0; j < 256; j++) 
	{
		left_tmp = 0.4142f * *left++ + clev * *centre   + slev * *left_sur++;
		right_tmp= 0.4142f * *right++ + clev * *centre++ + slev * *right_sur++;

		s16_samples[j * 2 ]    = (sint_16) (left_tmp  * 32767.0f);
		s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
		// printf("[0] = %.6f, [1] = %.6f\n", left_tmp, right_tmp);
	}
}

static void 
downmix_3f_2r_to_4ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	uint_32 j;
	float right_tmp, left_tmp, rear_right_tmp, rear_left_tmp;
	float clev,slev;
	float *centre = 0, *left = 0, *right = 0, *left_sur = 0, *right_sur = 0;

	left      = samples[0];
	centre    = samples[1];
	right     = samples[2];
	left_sur  = samples[3];
	right_sur = samples[4];

	clev = cmixlev_lut[bsi->cmixlev];
	slev = smixlev_lut[bsi->surmixlev];

	for (j = 0; j < 256; j++) 
	{
		left_tmp = 0.4142f * *left++  + clev * *centre;
		right_tmp= 0.4142f * *right++ + clev * *centre++;
		rear_left_tmp = 0.4142f * *left_sur++;
		rear_right_tmp = 0.4142f * *right_sur++;

		s16_samples[j * 4 ]    = (sint_16) (left_tmp  * 32767.0f);
		s16_samples[j * 4 + 1] = (sint_16) (right_tmp * 32767.0f);
		s16_samples[j * 4 + 2] = (sint_16) (rear_left_tmp  * 32767.0f);
		s16_samples[j * 4 + 3] = (sint_16) (rear_right_tmp * 32767.0f);
#if 0
		printf("[0] = %.6f, [1] = %.6f, [2] = %.6f, [3] = %.6f\n",
				left_tmp, right_tmp, rear_left_tmp, rear_right_tmp);
#endif
	}
}

static void 
downmix_3f_2r_to_6ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	uint_32 j;
	float right_tmp, centre_tmp, left_tmp, rear_right_tmp, rear_left_tmp, lfe_tmp;
	float clev,slev;
	float *centre = 0, *left = 0, *right = 0, *left_sur = 0, *right_sur = 0, *lfe = 0;

	left      = samples[0];
	centre    = samples[1];
	right     = samples[2];
	left_sur  = samples[3];
	right_sur = samples[4];
	lfe	  = samples[5];

	clev = cmixlev_lut[bsi->cmixlev];
	slev = smixlev_lut[bsi->surmixlev];

	for (j = 0; j < 256; j++) 
	{
		left_tmp = 0.4142f * *left++;
		right_tmp= 0.4142f * *right++;
		centre_tmp = 0.4142f * *centre++;
		rear_left_tmp = 0.4142f * *left_sur++;
		rear_right_tmp = 0.4142f * *right_sur++;
		lfe_tmp = bsi->lfeon ? 0.4142f * *lfe++ : (float)0.0;

		s16_samples[j * 6 ]    = (sint_16) (left_tmp  * 32767.0f);
		s16_samples[j * 6 + 1] = (sint_16) (right_tmp * 32767.0f);
		s16_samples[j * 6 + 2] = (sint_16) (rear_left_tmp * 32767.0f);
		s16_samples[j * 6 + 3] = (sint_16) (rear_right_tmp * 32767.0f);
		s16_samples[j * 6 + 4] = (sint_16) (centre_tmp  * 32767.0f);
		s16_samples[j * 6 + 5] = (sint_16) (lfe_tmp * 32767.0f);
#if 0
		printf("[0] = %.6f, [1] = %.6f, [2] = %.6f, [3] = %.6f, [4] = %.6f, [5] = %.6f\n",
				left_tmp, right_tmp, rear_left_tmp, rear_right_tmp,
				centre_tmp, lfe_tmp);
#endif
	}
}

static void
downmix_2f_2r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	uint_32 j;
	float right_tmp;
	float left_tmp;
	float slev;
	float *left = 0, *right = 0, *left_sur = 0, *right_sur = 0;

	left      = samples[0];
	right     = samples[1];
	left_sur  = samples[2];
	right_sur = samples[3];

	slev = smixlev_lut[bsi->surmixlev];

	for (j = 0; j < 256; j++) 
	{
		left_tmp = 0.4142f * *left++  + slev * *left_sur++;
		right_tmp= 0.4142f * *right++ + slev * *right_sur++;

		s16_samples[j * 2 ]    = (sint_16) (left_tmp  * 32767.0f);
		s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
	}
}

static void
downmix_3f_1r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	uint_32 j;
	float right_tmp;
	float left_tmp;
	float clev,slev;
	float *centre = 0, *left = 0, *right = 0, *sur = 0;

	left      = samples[0];
	centre    = samples[1];
	right     = samples[2];
	//Mono surround
	sur = samples[3];

	clev = cmixlev_lut[bsi->cmixlev];
	slev = smixlev_lut[bsi->surmixlev];

	for (j = 0; j < 256; j++) 
	{
		left_tmp = 0.4142f * *left++  + clev * *centre++ + slev * *sur;
		right_tmp= 0.4142f * *right++ + clev * *centre   + slev * *sur++;

		s16_samples[j * 2 ]    = (sint_16) (left_tmp  * 32767.0f);
		s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
	}
}


static void
downmix_2f_1r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	uint_32 j;
	float right_tmp;
	float left_tmp;
	float slev;
	float *left = 0, *right = 0, *sur = 0;

	left      = samples[0];
	right     = samples[1];
	//Mono surround
	sur = samples[2];

	slev = smixlev_lut[bsi->surmixlev];

	for (j = 0; j < 256; j++) 
	{
		left_tmp = 0.4142f * *left++  + slev * *sur;
		right_tmp= 0.4142f * *right++ + slev * *sur++;

		s16_samples[j * 2 ]    = (sint_16) (left_tmp  * 32767.0f);
		s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
	}
}

static void
downmix_3f_0r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	uint_32 j;
	float right_tmp;
	float left_tmp;
	float clev;
	float *centre = 0, *left = 0, *right = 0;

	left      = samples[0];
	centre    = samples[1];
	right     = samples[2];

	clev = cmixlev_lut[bsi->cmixlev];

	for (j = 0; j < 256; j++) 
	{
		left_tmp = 0.4142f * *left++  + clev * *centre; 
		right_tmp= 0.4142f * *right++ + clev * *centre++;   

		s16_samples[j * 2 ]    = (sint_16) (left_tmp  * 32767.0f);
		s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
	}
}
				
static void
downmix_2f_0r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	uint_32 j;
	float *left = 0, *right = 0;

	left      = samples[0];
	right     = samples[1];

	for (j = 0; j < 256; j++) 
	{
		s16_samples[j * 2 ]    = (sint_16) (*left++  * 32767.0f);
		s16_samples[j * 2 + 1] = (sint_16) (*right++ * 32767.0f);
	}
}

static void
downmix_1f_0r_to_2ch(float *centre,sint_16 *s16_samples)
{
	uint_32 j;
	float tmp;

	//Mono program!

	for (j = 0; j < 256; j++) 
	{
		tmp =  32767.0f * 0.7071f * *centre++;

		s16_samples[j * 2 ] = s16_samples[j * 2 + 1] = (sint_16) tmp;
	}
}

//
// Downmix into 2 or 4 channels  (4 ch isn't in quite yet)
//
// The downmix function names have the following format
//
// downmix_Xf_Yr_to_[2|4|6]ch[_dolby]
//
// where X        = number of front channels
//       Y        = number of rear channels
//       [2|4]    = number of output channels
//       [_dolby] = with or without dolby surround mix
//

void downmix(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
{
	if(bsi->acmod > 7)
		dprintf("(downmix) invalid acmod number\n"); 

	//
	//There are two main cases, with or without Dolby Surround
	//
	if(ac3_config.flags & AC3_DOLBY_SURR_ENABLE)
	{
		fprintf(stderr,"Dolby Surround Mixes not currently enabled\n");
		exit(1);
	}

	//Non-Dolby surround downmixes
	switch(bsi->acmod)
	{
		// 3/2
		case 7:
			switch (ac3_config.num_output_ch) {
			case 2:
				downmix_3f_2r_to_2ch(bsi,samples,s16_samples);
				break;
			case 4:
				downmix_3f_2r_to_4ch(bsi,samples,s16_samples);
				break;
			case 6:
				downmix_3f_2r_to_6ch(bsi,samples,s16_samples);
				break;
			default:
				fprintf(stderr,"unsupported 3/2 channels %d\n", ac3_config.num_output_ch);
				exit(1);
			}
		break;

		// 2/2
		case 6:
			if (ac3_config.num_output_ch != 2) {
				fprintf(stderr,"unsupported 2/2 channels %d\n", ac3_config.num_output_ch);
				exit(1);
			}
			downmix_2f_2r_to_2ch(bsi,samples,s16_samples);
		break;

		// 3/1
		case 5:
			if (ac3_config.num_output_ch != 2) {
				fprintf(stderr,"unsupported 3/1 channels %d\n", ac3_config.num_output_ch);
				exit(1);
			}
			downmix_3f_1r_to_2ch(bsi,samples,s16_samples);
		break;

		// 2/1
		case 4:
			if (ac3_config.num_output_ch != 2) {
				fprintf(stderr,"unsupported 2/1 channels %d\n", ac3_config.num_output_ch);
				exit(1);
			}
			downmix_2f_1r_to_2ch(bsi,samples,s16_samples);
		break;

		// 3/0
		case 3:
			if (ac3_config.num_output_ch != 2) {
				fprintf(stderr,"unsupported 3/0 channels %d\n", ac3_config.num_output_ch);
				exit(1);
			}
			downmix_3f_0r_to_2ch(bsi,samples,s16_samples);
		break;

		// 2/0
		case 2:
			if (ac3_config.num_output_ch != 2) {
				fprintf(stderr,"unsupported 2/0 channels %d\n", ac3_config.num_output_ch);
				exit(1);
			}
			downmix_2f_0r_to_2ch(bsi,samples,s16_samples);
		break;

		// 1/0
		case 1:
			if (ac3_config.num_output_ch != 2) {
				fprintf(stderr,"unsupported 1/0 channels %d\n", ac3_config.num_output_ch);
				exit(1);
			}
			downmix_1f_0r_to_2ch(samples[0],s16_samples);
		break;

		// 1+1
		case 0:
			if (ac3_config.num_output_ch != 2) {
				fprintf(stderr,"unsupported 1/1 channels %d\n", ac3_config.num_output_ch);
				exit(1);
			}
			downmix_1f_0r_to_2ch(samples[ac3_config.dual_mono_ch_sel],s16_samples);
		break;
	}
}



#if 0 

	//the dolby mixes lay here for the time being
	switch(bsi->acmod)
	{
		// 3/2
		case 7:
			left      = samples[0];
			centre    = samples[1];
			right     = samples[2];
			left_sur  = samples[3];
			right_sur = samples[4];

			for (j = 0; j < 256; j++) 
			{
				right_tmp = 0.2265f * *left_sur++ + 0.2265f * *right_sur++;
				left_tmp  = -1 * right_tmp;
				right_tmp += 0.3204f * *right++ + 0.2265f * *centre;
				left_tmp  += 0.3204f * *left++  + 0.2265f * *centre++;

				samples[1][j] = right_tmp;
				samples[0][j] = left_tmp;
			}

		break;

		// 2/2
		case 6:
			left      = samples[0];
			right     = samples[1];
			left_sur  = samples[2];
			right_sur = samples[3];

			for (j = 0; j < 256; j++) 
			{
				right_tmp = 0.2265f * *left_sur++ + 0.2265f * *right_sur++;
				left_tmp  = -1 * right_tmp;
				right_tmp += 0.3204f * *right++;
				left_tmp  += 0.3204f * *left++ ;

				samples[1][j] = right_tmp;
				samples[0][j] = left_tmp;
			}
		break;

		// 3/1
		case 5:
			left      = samples[0];
			centre    = samples[1];
			right     = samples[2];
			//Mono surround
			right_sur = samples[3];

			for (j = 0; j < 256; j++) 
			{
				right_tmp =  0.2265f * *right_sur++;
				left_tmp  = -1 * right_tmp;
				right_tmp += 0.3204f * *right++ + 0.2265f * *centre;
				left_tmp  += 0.3204f * *left++  + 0.2265f * *centre++;

				samples[1][j] = right_tmp;
				samples[0][j] = left_tmp;
			}
		break;

		// 2/1
		case 4:
			left      = samples[0];
			right     = samples[1];
			//Mono surround
			right_sur = samples[2];

			for (j = 0; j < 256; j++) 
			{
				right_tmp =  0.2265f * *right_sur++;
				left_tmp  = -1 * right_tmp;
				right_tmp += 0.3204f * *right++; 
				left_tmp  += 0.3204f * *left++;

				samples[1][j] = right_tmp;
				samples[0][j] = left_tmp;
			}
		break;

		// 3/0
		case 3:
			left      = samples[0];
			centre    = samples[1];
			right     = samples[2];

			for (j = 0; j < 256; j++) 
			{
				right_tmp = 0.3204f * *right++ + 0.2265f * *centre;
				left_tmp  = 0.3204f * *left++  + 0.2265f * *centre++;

				samples[1][j] = right_tmp;
				samples[0][j] = left_tmp;
			}
		break;

		// 2/0
		case 2:
		//Do nothing!
		break;

		// 1/0
		case 1:
			//Mono program!
			right = samples[0];

			for (j = 0; j < 256; j++) 
			{
				right_tmp = 0.7071f * *right++;

				samples[1][j] = right_tmp;
				samples[0][j] = right_tmp;
			}
			
		break;

		// 1+1
		case 0:
			//Dual mono, output selected by user
			right = samples[ac3_config.dual_mono_ch_sel];

			for (j = 0; j < 256; j++) 
			{
				right_tmp = 0.7071f * *right++;

				samples[1][j] = right_tmp;
				samples[0][j] = right_tmp;
			}
		break;
#endif
