/*****************************************************************************
 * Copyright (c) 2018, Broadcom Inc.                                         *
 *                                                                           *
 * All Rights Reserved.                                                      *
 *                                                                           *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Inc.;             *
 * the contents of this file may not be disclosed to third parties, copied   *
 * or duplicated in any form, in whole or in part, without the prior         *
 * written permission of Broadcom Inc.                                       *
 *****************************************************************************/
/**
 * @file vend_entry_au.c
 * @brief entry function for audio overlay
 */

#include "dsp_sdk_types.h"
#include "overlay.h"
#include "vend_au.h"
#include "dsp_sdk_trace.h"

#define SBM_DSP_DEBUG_CODE_INCLUDE

//#define DEBUG_CALC_MCPS

#ifdef DEBUG_CALC_MCPS	//hoon_test to measure complexity
#include <xtensa/xtruntime.h>
#endif

#ifdef DEBUG_CALC_MCPS	//hoon_test
UINT32 time_start;
UINT32 time_end;
UINT32 time_diff;

UINT32 time_peak_audio = 0;

UINT32 fram_cnt_audio=0;
#endif

#define SBM_NUM_NORMAL 100    /* about 1.96sec */

/******************  fast 5.54%, slow 2.76%  *******************/
#define SBM_FAST_SPEED 67380    // 840       2.76%
#define SBM_SLOW_SPEED 63730   // 888        2.76%

//////////////////////////////////////////////////////////////////////
#define SBM_NORMAL_OUTPUT_NUM 864
#define SBM_FAST_OUTPUT_NUM 840
#define SBM_SLOW_OUTPUT_NUM 888
//////////////////////////////////////////////////////////////////////

//#define SS_AUDIO_TEST_MODE
#define SS_AMB_TEST_MODE

#define SSC_SUPPORT_SBM
#define SSC_SUPPORT_PLC


extern INT32 au_task_GetAuBufLevel(UINT8 *buff_depth, UINT32 *que_count);
#ifdef ENABLE_SS_AUDIO
//  #ifndef SS_AUDIO_TEST_MODE
#include "ssc.h"
//  #endif
#endif

#ifdef SS_AUDIO_TEST_MODE
#define TEST_TONE_SIZE 32
const INT16 g_testTone1dot5K48K16bit[TEST_TONE_SIZE] =
{
    0,      6393,   12540,  18205,  23170,  27246,  30274,  32138,
    32767,  32138,  30274,  27246,  23170,  18205,  12540,  6393,
    0,      -6393,  -12540, -18205, -23170, -27246, -30274, -32138,
    -32768, -32138, -30274, -27246, -23170, -18205, -12540, -6393
};
#endif

#ifdef SSC_SUPPORT_SBM
enum
{
    SBM_STATE_IDLE = 0,
    SBM_STATE_TRIGGERED,
    SBM_STATE_OUTPUT_SAMPLE_CHANGED
};

UINT8 g_SBM_State = SBM_STATE_IDLE;
#endif

/**
 * Declaration of overlay info and entry function. Do not change it.
 */
INT32 vend_entry_au(OVERLAY_ST_t *p_overlay_parameter) __attribute__((section(".iram0_overlay_entry.text")));

OVERLAY_INFO_t ovl_au_info __attribute__((section(".dram0_overlay_info.data"))) =
{
    MAGIC_PATTERN_AU,
    (void *)vend_entry_au
};



/**
 * function prototypes
 */
INT32 vend_ss_audio_init(OVERLAY_ST_t *p_overlay_parameter);
INT32 vend_ss_audio_exec(OVERLAY_ST_t *p_overlay_parameter);
INT32 vend_ambient_init(OVERLAY_ST_t *p_overlay_parameter);
INT32 vend_ambient_exec(OVERLAY_ST_t *p_overlay_parameter);



/**
 * Variable declarations.
 * Note: Total scratch memory size can't exceed 128KB
 */
//UINT32 g_ss_audio_scratch[SS_AUDIO_SCRATCH_MEM_SIZE_INT32];  /* Scratch memory for ss_audio */
UINT32 g_ambient_scratch[AMBIENT_SCRATCH_MEM_SIZE_INT32];    /* Scratch memory for ambient */


/**
 * Entry function of audio overlay
 */
INT32 vend_entry_au(OVERLAY_ST_t *p_overlay_parameter)
{
    INT32 ret = VEND_SUCCESS;

    switch(p_overlay_parameter->algo_idx)
    {
        case ALGO_IDX_SS_AUDIO_INIT:
            ret = vend_ss_audio_init(p_overlay_parameter);
            break;
        case ALGO_IDX_SS_AUDIO_EXEC:
            ret = vend_ss_audio_exec(p_overlay_parameter);
            break;
        case ALGO_IDX_AMBIENT_INIT:
            ret = vend_ambient_init(p_overlay_parameter);
            break;
        case ALGO_IDX_AMBIENT_EXEC:
            ret = vend_ambient_exec(p_overlay_parameter);
            break;
        default:
            ret = MIN_INT32;
            break;
    }
    return ret;
}



#ifdef SSC_SUPPORT_SBM
#define NUM_MIN_NORMALSPEED 30
INT16 num_min_normalspeed;    // After transition (slow or fast play), Normal play need to be played at least NUM_MIN_NORMALSPEED
#endif
/**
 * ss_audio init process.
 */
INT32 FrameCnt;
INT32 PLC_Frame;
INT32 vend_ss_audio_init(OVERLAY_ST_t *p_overlay_parameter)
{
    INT32 ret = VEND_SUCCESS;

    TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL] Init SS audio start");

#ifdef ENABLE_SS_AUDIO
    SS_AUDIO_EXT_ST_t *p_ss_audio_external_param = &p_overlay_parameter->ovl_paramter.ovl_au.ss_audio_external_parameter;
    void *dec;
    UINT32  dram_base_addr = (UINT32)p_ss_audio_external_param->dram_dec_base_addr;
    //TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL] vend_ss_audio_init start. sf=%d", p_ss_audio_external_param->sampling_rate);

    p_ss_audio_external_param->mem_offset = 0;
    p_ss_audio_external_param->frame_size = SS_AUDIO_FRAME_SIZE<<2;
    p_ss_audio_external_param->frame_cnt = 0;


    //allocate memory
    MALLOC_BYTE(p_ss_audio_external_param->dec_inst, ssc_decoder_get_size(SS_AUDIO_CHANNELS), dram_base_addr, p_ss_audio_external_param->mem_offset);
    MALLOC_BYTE(p_ss_audio_external_param->in_buf, SS_AUDIO_MAX_BITSTREAM_SIZE, dram_base_addr, p_ss_audio_external_param->mem_offset);


#ifndef SSC_SUPPORT_SBM
    MALLOC_BYTE(p_ss_audio_external_param->out_buf, SS_AUDIO_FRAME_SIZE*4, dram_base_addr, p_ss_audio_external_param->mem_offset);
#else
    MALLOC_BYTE(p_ss_audio_external_param->out_buf, 1200*4, dram_base_addr, p_ss_audio_external_param->mem_offset);
    p_ss_audio_external_param->trigger_SBM = 0;
    p_ss_audio_external_param->target_buffer_depth = 0;
	num_min_normalspeed = NUM_MIN_NORMALSPEED;
#endif

    dec = p_ss_audio_external_param->dec_inst;

    if(p_ss_audio_external_param->mem_offset > p_ss_audio_external_param->dram_dec_size)
    {
        TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL] SS_audio dRAM buffer is not enough!");
        ret = -1;
        return ret;
    }

#if 0
    ret = ssc_decoder_init(dec, SS_AUDIO_CHANNELS, /*sampling_rate*/44100, p_overlay_parameter->vend_sbm_enable);
#else
    TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL] sampling rate : %d, SBM enabled : %d", p_ss_audio_external_param->sampling_rate, p_overlay_parameter->vend_sbm_enable);
    ret = ssc_decoder_init(dec, SS_AUDIO_CHANNELS, p_ss_audio_external_param->sampling_rate, p_overlay_parameter->vend_sbm_enable);
	FrameCnt = 0;
	PLC_Frame = 0;
#endif

#ifdef SSC_SUPPORT_SBM
    g_SBM_State = SBM_STATE_IDLE;
    ssc_set_SBMspeed_test(SBM_SLOW_SPEED,SBM_FAST_SPEED,SBM_NUM_NORMAL);
#endif

    if(ret<0)
    {
        TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL] ssc_decoder_init error!");
    }
    else
    {
        TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL] ssc_decoder_init success!");
    }
#endif

    TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL] Init SS audio end");

    return ret;
}



/**
 * ss_audio execute process.
 */
INT32 vend_ss_audio_exec(OVERLAY_ST_t *p_overlay_parameter)
{

    INT32 ret = VEND_SUCCESS;
#ifdef ENABLE_SS_AUDIO  //hoon_test
#ifndef SS_AUDIO_TEST_MODE
    void *dec;
    UINT8 syncword[4];
    INT32 n;
#endif
#endif
#ifdef SSC_SUPPORT_SBM
    INT16 sbm_direction = 0;       /* 0: no buf level change, 1: start transition to decrease buf level, -1: start transition to increase buf level */
    INT16 buf_level_offset = 0;    /* the amount of buf level change, formula : ABS(current buf detpth - target buf depth) * 1frame  */
	UINT8 buff_depth = 0;
	UINT32 que_count = 0;
#endif

#ifdef SBM_DSP_DEBUG_CODE_INCLUDE
    INT32 sbm_get_state;
#endif

#ifdef HACK_VEND_AU_TOBE_SBC
    //au_task_1stCodecDecode();
    (*g_hack_ptrs_au)();
    g_vend_au_cnt++;

    if((g_vend_au_cnt & 0xFF) == 0)
    {
        TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU OVL] SS audio exec cnt0x%x (A2DP)", g_vend_au_cnt);
    }

    return ret;
#endif

#ifdef ENABLE_SS_AUDIO

    SS_AUDIO_EXT_ST_t *p_ss_audio_external_param = &p_overlay_parameter->ovl_paramter.ovl_au.ss_audio_external_parameter;

#ifdef SS_AUDIO_TEST_MODE
    INT32 i;
    INT32 j=0;
    INT16 *out = (INT16 *) p_ss_audio_external_param->out_buf;   //size = 864(sample)* 2(channel) * 2(byte/sample)

    for(i=0 ; i<(SS_AUDIO_FRAME_SIZE*2); i+=2)
    {
        out[i] = g_testTone1dot5K48K16bit[j]>>2;
        out[i+1] = g_testTone1dot5K48K16bit[j]>>2;

        j++;
        if(j == 32)
        {
            j=0;
        }
    }

    p_ss_audio_external_param->output_samples = SS_AUDIO_FRAME_SIZE;
    p_ss_audio_external_param->frame_cnt++;
#else
    dec = p_ss_audio_external_param->dec_inst;

    for(n=0; n<4;n++)
        syncword[n] = p_ss_audio_external_param->in_buf[n];


#ifdef SSC_SUPPORT_SBM
    if(p_ss_audio_external_param->trigger_SBM)
    {
        if(p_ss_audio_external_param->current_buffer_depth > p_ss_audio_external_param->target_buffer_depth)
        {
            TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SBM] Trigger Game mode!!");
            buf_level_offset = p_ss_audio_external_param->current_buffer_depth - p_ss_audio_external_param->target_buffer_depth;
            sbm_direction = 1;
            g_SBM_State = SBM_STATE_TRIGGERED;
            TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SBM] Trigger SBM %d -> %d",
                                              p_ss_audio_external_param->current_buffer_depth,
                                              p_ss_audio_external_param->target_buffer_depth);
   //         au_task_NotifyJitterBufLevelChange(EVENTTYPE_JITTER_BUFFER_LEVEL_CHANGE_STARTED, 0);
        }
        else if(p_ss_audio_external_param->current_buffer_depth < p_ss_audio_external_param->target_buffer_depth)
        {
            TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SBM] Trigger Normal mode!!");
            buf_level_offset = p_ss_audio_external_param->target_buffer_depth - p_ss_audio_external_param->current_buffer_depth;
            sbm_direction = -1;
            g_SBM_State = SBM_STATE_TRIGGERED;
            TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SBM] Trigger SBM %d -> %d",
                                              p_ss_audio_external_param->current_buffer_depth,
                                              p_ss_audio_external_param->target_buffer_depth);
 //           au_task_NotifyJitterBufLevelChange(EVENTTYPE_JITTER_BUFFER_LEVEL_CHANGE_STARTED, 0);
        }
        else
        {
            TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SBM] invalid target_buffer_depth %d, current depth %d",
                                              p_ss_audio_external_param->target_buffer_depth,
                                              p_ss_audio_external_param->current_buffer_depth);
        }

        /* Reset variables for the next trigger */
        p_ss_audio_external_param->trigger_SBM = 0;
        p_ss_audio_external_param->target_buffer_depth = 0;
        p_ss_audio_external_param->current_buffer_depth = 0;
    }

    ssc_sbm_interface(sbm_direction, buf_level_offset);
#endif
	if(p_ss_audio_external_param->plc_frame == 0)
	{
		if(syncword[0]==0xFF && syncword[1]==0xEE)
		{
#ifdef DEBUG_CALC_MCPS
			time_start = xthal_get_ccount();
#endif	
			p_ss_audio_external_param->output_samples = ssc_decode(dec, p_ss_audio_external_param->in_buf, (INT16 *)p_ss_audio_external_param->out_buf, SS_AUDIO_FRAME_SIZE, p_ss_audio_external_param->plc_frame, p_ss_audio_external_param->channel); // out will have pcm audio data
			FrameCnt++;
#ifdef DEBUG_CALC_MCPS
			time_end = xthal_get_ccount();
			time_diff = time_end - time_start;
			if(time_diff > time_peak_audio)
				time_peak_audio = time_diff;

			fram_cnt_audio++;
			if(fram_cnt_audio % 133 ==0)
			{
				TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP SP_OVL][ssc] ssc audio excution cycle(Peak) : %d", time_peak_audio);
				fram_cnt_audio = 0;
			}
#endif
		}
		else
		{
			p_ss_audio_external_param->output_samples = -1;
		}
	}
	else
	{
#ifdef SSC_SUPPORT_PLC    //   p_ss_audio_external_param->plc_frame ==  1 case.

#ifdef DEBUG_CALC_MCPS
	time_start = xthal_get_ccount();
#endif	
		p_ss_audio_external_param->output_samples = ssc_decode(dec, p_ss_audio_external_param->in_buf, (INT16 *)p_ss_audio_external_param->out_buf, SS_AUDIO_FRAME_SIZE, p_ss_audio_external_param->plc_frame, p_ss_audio_external_param->channel);
		FrameCnt++;
		PLC_Frame++;
#ifdef DEBUG_CALC_MCPS
	time_end = xthal_get_ccount();
	time_diff = time_end - time_start;
	if(time_diff > time_peak_audio)
		time_peak_audio = time_diff;

	fram_cnt_audio++;
	if(fram_cnt_audio % 133 ==0)
	{
		TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP SP_OVL][ssc] ssc audio PLC excution cycle(Peak) : %d", time_peak_audio);
		TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP SP_OVL][ssc] ssc audio DBG_MSG Total Frame : %d, PLC Frame : %d", FrameCnt, PLC_Frame);
		fram_cnt_audio = 0;
	}
#endif

#else
		INT16 *dec_out = (INT16 *)p_ss_audio_external_param->out_buf;
		INT16 num_output;
		INT16 state_info = ssc_SBM_getstatus();
		
		if(state_info == 0)
		{
			num_output = SBM_NORMAL_OUTPUT_NUM;
		}
		else if(state_info == 1)
		{
			num_output = SBM_SLOW_OUTPUT_NUM;
		}
		else if(state_info == 2)
		{
			num_output = SBM_FAST_OUTPUT_NUM;
		}
		else
		{
			num_output = 864;
		}

		if(p_ss_audio_external_param->channel != SS_JOINT_CHANNEL)
		{
			for(n=0; n<num_output; n++)
			{
				dec_out[n] = 0;
			}
		}
		else
		{
			for(n=0; n<(num_output*2); n++)
			{
				dec_out[n] = 0;
			}
		}

        p_ss_audio_external_param->output_samples = num_output;
#endif
    }

#ifndef SSC_SUPPORT_SBM
    if(p_ss_audio_external_param->output_samples != SS_AUDIO_FRAME_SIZE)
    {
        TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SS AUDIO DEC] decode fail");
        ret = VEND_FAIL;
    }
    else
    {
        p_ss_audio_external_param->frame_cnt++;
    }
#else
	au_task_GetAuBufLevel(&buff_depth, &que_count);
	//TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SBM] buff_depth: %d, que_count: %d", buff_depth, que_count);
    if (g_SBM_State == SBM_STATE_TRIGGERED && p_ss_audio_external_param->output_samples != SS_AUDIO_FRAME_SIZE)
    {
        g_SBM_State = SBM_STATE_OUTPUT_SAMPLE_CHANGED;
    }
    else if (g_SBM_State == SBM_STATE_OUTPUT_SAMPLE_CHANGED && p_ss_audio_external_param->output_samples == SS_AUDIO_FRAME_SIZE)
    {
		if(num_min_normalspeed == 0)
		{
			g_SBM_State = SBM_STATE_IDLE;
			TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SBM] Send : EVENTTYPE_JITTER_BUFFER_LEVEL_CHANGE_COMPLETED, buff_depth : %d", buff_depth);
			au_task_NotifyJitterBufLevelChange(EVENTTYPE_JITTER_BUFFER_LEVEL_CHANGE_COMPLETED, buff_depth);

			num_min_normalspeed = NUM_MIN_NORMALSPEED;
		}
		else
		{
			num_min_normalspeed--;
		}
    }

    if(p_ss_audio_external_param->output_samples<500 ||  p_ss_audio_external_param->output_samples>1200)
    {
        TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SS AUDIO DEC] decode fail");
        ret = VEND_FAIL;
    }
    else
    {
        /* check if number of output sample is multiple of 8*/
        if (p_ss_audio_external_param->output_samples % 8)
        {
            TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SS AUDIO DEC] output samples is not multiple of 8 : %d", p_ss_audio_external_param->output_samples);
            p_ss_audio_external_param->output_samples = 0;
            ret = VEND_FAIL;
        }

        p_ss_audio_external_param->frame_cnt++;
    }
#endif

#ifdef SBM_DSP_DEBUG_CODE_INCLUDE
	sbm_get_state = ssc_SBM_getstatus();
	if(sbm_get_state == 0)
	{
		if(p_ss_audio_external_param->output_samples != SBM_NORMAL_OUTPUT_NUM)
		{
				TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SS AUDIO DEC] sbm state is not match with num of output samples(Normal)");
		}
	}
	else if(sbm_get_state == 1)
	{
		if(p_ss_audio_external_param->output_samples != SBM_SLOW_OUTPUT_NUM)    // 888 is for the "#define SBM_SLOW_SPEED 63730    /* 888    63700 ~ 63750   2.76% */"
		{
			TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SS AUDIO DEC] sbm state is not match with num of output samples(Slow)");
		}
	}
	else if(sbm_get_state == 2)  
	{
		if(p_ss_audio_external_param->output_samples != SBM_FAST_OUTPUT_NUM)  // 
		{
			TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SS AUDIO DEC] sbm state is not match with num of output samples(Fast)");
		}
	}
	else if(sbm_get_state == 3 || sbm_get_state == 4)
	{
		TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SS AUDIO DEC] On the SBM transition, new transition is started");
	}
	else
	{
		TRACE(TRACE_INFO, MODULE_ID_DSP, "[DSP AU_OVL][SS AUDIO DEC] Unknown SBM state info");
	}
#endif



#endif
#endif

    return ret;
}



/**
 * ambient init process.
 */
INT32 vend_ambient_init(OVERLAY_ST_t *p_overlay_parameter)
{
    INT32 ret = VEND_SUCCESS;
#ifdef ENABLE_AMBIENT_ALGO
    AMBIENT_EXT_ST_t *p_ambient_external_param = &p_overlay_parameter->ovl_paramter.ovl_au.ambient_external_parameter;

    p_ambient_external_param->mem_offset = 0;
    //allocate memory
//    MALLOC_BYTE(p_ambient_external_param->in_buf, AMBIENT_PCM_N, g_ambient_scratch, p_ambient_external_param->mem_offset);
//    MALLOC_BYTE(p_ambient_external_param->out_buf, AMBIENT_PCM_N, g_ambient_scratch, p_ambient_external_param->mem_offset);
    MALLOC_BYTE(p_ambient_external_param->in_buf, (AMBIENT_PCM_N<<2), g_ambient_scratch, p_ambient_external_param->mem_offset);
    MALLOC_BYTE(p_ambient_external_param->out_buf, (AMBIENT_PCM_N<<2), g_ambient_scratch, p_ambient_external_param->mem_offset);

#ifndef SS_AMB_TEST_MODE
    Samsung_Ambient_Init(p_ambient_external_param->sampling_rate, p_ambient_external_param->ambient_mode, AMBIENT_BITWIDTH);
#endif

#endif
    return ret;
}



/**
 * ambient execute process.
 */
INT32 vend_ambient_exec(OVERLAY_ST_t *p_overlay_parameter)
{
    INT32 ret = VEND_SUCCESS;
#ifdef ENABLE_AMBIENT_ALGO

    AMBIENT_EXT_ST_t *p_ambient_external_param = &p_overlay_parameter->ovl_paramter.ovl_au.ambient_external_parameter;

    p_ambient_external_param->ambient_volume = 7;

#ifdef SS_AMB_TEST_MODE
    memcpy(g_overlay_parameter.ovl_paramter.ovl_au.ambient_external_parameter.out_buf,
           g_overlay_parameter.ovl_paramter.ovl_au.ambient_external_parameter.in_buf,
           (AMBIENT_PCM_N<<2));

    /* Simulate AMB process time */
    au_task_TimerDelay(VEND_AMB_PROCESS_TIME);

#else
    Samsung_Ambient_Exe(p_ambient_external_param->in_buf, p_ambient_external_param->out_buf, AMBIENT_PCM_N, p_ambient_external_param->ambient_mode, AMBIENT_WARN_DETECT, p_ambient_external_param->ambient_volume);
#endif

#endif

    return ret;
}

