/*
 * Copyright (C) 2003 - 2016 Sony Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ldac.h"


#define LDAC_TH_LOWENERGY_L _scalar(225.47)
#define LDAC_TH_LOWENERGY_M _scalar(897.61)
#define LDAC_TH_LOWENERGY_H _scalar(3573.44)

#define LDAC_TH_CENTROID    _scalar(45.0)
#define LDAC_TH_ZERODIV     _scalar(1.0e-6)

/***************************************************************************************************
    Calculate Pseudo Spectrum and Low Band Energy
***************************************************************************************************/
static SCALAR calc_mdct_pseudo_spectrum_ldac(
SCALAR *p_spec,
SCALAR *p_psd,
int n)
{
    int isp;
    SCALAR low_energy, tmp;
    SCALAR y0, y1, y2;

    {
        y1 = p_spec[0];
        y2 = p_spec[1];
        tmp = y1 * y1 + y2 * y2;
        low_energy = tmp;
        p_psd[0] = sqrt(tmp);
    }

    for (isp = 1; isp < LDAC_NSP_LOWENERGY; isp++) {
        y0 = y1;
        y1 = y2;
        y2 = p_spec[isp+1];
        tmp = y1 * y1 + (y0-y2) * (y0-y2);
        low_energy += tmp;
        p_psd[isp] = sqrt(tmp);
    }

    for (isp = LDAC_NSP_LOWENERGY; isp < n-1; isp++) {
        y0 = y1;
        y1 = y2;
        y2 = p_spec[isp+1];
        tmp = y1 * y1 + (y0-y2) * (y0-y2);
        p_psd[isp] = sqrt(tmp);
    }

    {
        tmp = y1 * y1 + y2 * y2;
        p_psd[n-1] = sqrt(tmp);
    }

    return low_energy;
}

/***************************************************************************************************
    Calculate Pseudo Spectrum Centroid
***************************************************************************************************/
static SCALAR calc_spectral_centroid_ldac(
SCALAR *p_spec,
int nsp)
{
    int isp;
    SCALAR centroid;
    SCALAR s1, s2;

    s1 = s2 = _scalar(0.0);
    for (isp = 0; isp < nsp; isp++) {
        s1 += (SCALAR)isp * *p_spec;
        s2 += *p_spec++;
    }

    if (s2 < LDAC_TH_ZERODIV) {
        centroid = _scalar(0.0);
    }
    else {
        centroid = s1 / s2;
    }

    return centroid;
}

/***************************************************************************************************
    Calculate Number of Zero Cross
***************************************************************************************************/
static int calc_zero_cross_number_ldac(
SCALAR *p_time,
int n)
{
    int i;
    int zero_cross = 0;
    SCALAR prev;

    prev = _scalar(0.0);
    for (i = 0; i < n; i++) {
        if (prev * *p_time < _scalar(0.0)) {
            zero_cross++;
        }
        prev = *p_time++;
    }

    return zero_cross;
}

/***************************************************************************************************
    Analyze Frame Status
***************************************************************************************************/
DECLSPEC int ana_frame_status_ldac(
SFINFO *p_sfinfo,
int nlnn)
{
    AC *p_ac;
    int ich;
    int nchs = p_sfinfo->cfg.ch;
    int nsmpl = npow2_ldac(nlnn+1);
    int cnt, zero_cross;
    int a_status[LDAC_PRCNCH];
    SCALAR low_energy, centroid;
    SCALAR a_psd_spec[LDAC_NSP_PSEUDOANA];

    for (ich = 0; ich < nchs; ich++) {
        p_ac = p_sfinfo->ap_ac[ich];

        low_energy = calc_mdct_pseudo_spectrum_ldac(p_ac->p_acsub->a_spec, a_psd_spec, LDAC_NSP_PSEUDOANA);

        centroid = calc_spectral_centroid_ldac(a_psd_spec, LDAC_NSP_PSEUDOANA);

        zero_cross = calc_zero_cross_number_ldac(p_ac->p_acsub->a_time, nsmpl);

        a_status[ich] = LDAC_FRMSTAT_LEV_0;
        if (low_energy < LDAC_TH_LOWENERGY_L) { 
            a_status[ich] = LDAC_FRMSTAT_LEV_3;
        }
        else {
            if (low_energy < LDAC_TH_LOWENERGY_M) {
                a_status[ich] = LDAC_FRMSTAT_LEV_2;
            }
            else if (low_energy < LDAC_TH_LOWENERGY_H) {
                a_status[ich] = LDAC_FRMSTAT_LEV_1;
            }

            cnt = p_ac->frmana_cnt;
            if ((centroid > LDAC_TH_CENTROID) && (zero_cross >= LDAC_TH_ZCROSNUM)) {
                cnt++;

                if (cnt >= LDAC_MAXCNT_FRMANA) {
                    cnt = LDAC_MAXCNT_FRMANA;
                    a_status[ich] = LDAC_FRMSTAT_LEV_2;
                }
                else if (a_status[ich] <= LDAC_FRMSTAT_LEV_1) {
                    a_status[ich]++;
                }
            }
            else {
                cnt = 0;
            }
            p_ac->frmana_cnt = cnt;
        }
    }

    if (nchs == LDAC_CHANNEL_1CH) {
        return a_status[0];
    }
    else {
        return min_ldac(a_status[0], a_status[1]);
    }
}