/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * 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.
 * -------------------------------------------------------------------
 */
/*
------------------------------------------------------------------------------

   PacketVideo Corp.
   MP3 Decoder Library

   Filename: pvmp3_stereo_proc.cpp

   Functions:

    pvmp3_st_mid_side
    pvmp3_st_intensity
    pvmp3_stereo_proc

------------------------------------------------------------------------------

pvmp3_st_mid_side

 INPUT AND OUTPUT DEFINITIONS

Input

   int32 xr[],      input channel
   int32 xl[],
   int32 Start,     Location of first element where stereo intensity is applied
   int32 Number     number of elements affected

 Returns

   int32 xl[],      generated stereo channel


------------------------------------------------------------------------------

pvmp3_st_intensity

 INPUT AND OUTPUT DEFINITIONS

Input

   int32 xr[],      input channel
   int32 xl[],
   int32 is_pos,    index to table is_ratio_factor[]
   int32 Start,     Location of first element where stereo intensity is applied
   int32 Number     number of elements affected

 Returns

   int32 xl[],      generated stereo channel


------------------------------------------------------------------------------

pvmp3_stereo_proc

 INPUT AND OUTPUT DEFINITIONS

Input

   int32 xr[],                    input channel
   int32 xl[],
   mp3ScaleFactors  *scalefac,    scale factors structure
   struct gr_info_s *gr_info,     granule structure
   mp3Header *info                mp3 header info
 Returns

   int32 xl[],      generated stereo channel


------------------------------------------------------------------------------
 FUNCTION DESCRIPTION

    stereo processing for mpeg1 layer III
    After requantization, the reconstructed values are processed for ms_stereo
    or intensity_stereo modes or both, before passing them to the synthesis
    filterbank

    In ms_stereo mode the values of the normalized middle/side channels
    M[l] and S[l] are transmitted instead of the left/right channel values
    L[l] and R[l]. From here, L[l] and R[l] are reconstructed

    Intensity_stereo is done by specifying the magnitude (via the
    scalefactors of the left channel) and a stereo position is_pos[sfb],
    which is transmitted instead of scalefactors of the right channel.
    The stereo position is used to derive the left and right channel signals

------------------------------------------------------------------------------
 REQUIREMENTS


------------------------------------------------------------------------------
 REFERENCES

 [1] ISO MPEG Audio Subgroup Software Simulation Group (1996)
     ISO 13818-3 MPEG-2 Audio Decoder - Lower Sampling Frequency Extension

------------------------------------------------------------------------------
 PSEUDO-CODE

------------------------------------------------------------------------------
*/


/*----------------------------------------------------------------------------
; INCLUDES
----------------------------------------------------------------------------*/

#include "pvmp3_stereo_proc.h"
#include "pv_mp3dec_fxd_op.h"
#include "pvmp3_tables.h"


/*----------------------------------------------------------------------------
; MACROS
; Define module specific macros here
----------------------------------------------------------------------------*/


/*----------------------------------------------------------------------------
; DEFINES
; Include all pre-processor statements here. Include conditional
; compile variables also.
----------------------------------------------------------------------------*/
#define N31 31

#define Q31_fmt(a)    (int32(double(0x7FFFFFFF)*(a)))

/*----------------------------------------------------------------------------
; LOCAL FUNCTION DEFINITIONS
; Function Prototype declaration
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; LOCAL STORE/BUFFER/POINTER DEFINITIONS
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/
/*
 *  TmpFac= tan(is_pos * (PI /12));
 *
 *  TmpFac /= (1 + TmpFac);
 *
 */

const int32  is_ratio_factor[8] = {0,
                                   Q31_fmt(0.21132486540519),   Q31_fmt(0.36602540378444),   Q31_fmt(0.50000000000000),
                                   Q31_fmt(0.63397459621556),   Q31_fmt(0.78867513459481),   Q31_fmt(1.00000000000000),
                                   0
                                  };

/*----------------------------------------------------------------------------
; EXTERNAL FUNCTION REFERENCES
; Declare functions defined elsewhere and referenced in this module
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; EXTERNAL GLOBAL STORE/BUFFER/POINTER REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

#if __has_attribute(no_sanitize)
// deliberately playing near overflow points of int32
__attribute__((no_sanitize("integer")))
#endif
void pvmp3_st_mid_side(int32 xr[SUBBANDS_NUMBER*FILTERBANK_BANDS],
                       int32 xl[SUBBANDS_NUMBER*FILTERBANK_BANDS],
                       int32 Start,
                       int32 Number)
{

    int32 *pt_xr  = &xr[Start];
    int32 *pt_xl  = &xl[Start];

    for (int32 i = Number >> 1; i != 0; i--)
    {
        int32 xxr = *(pt_xr) << 1;
        int32 xxl = *(pt_xl) << 1;
        *(pt_xr++)  = fxp_mul32_Q32((xxr + xxl), Q31_fmt(0.70710678118655));   /* Sum */
        *(pt_xl++)  = fxp_mul32_Q32((xxr - xxl), Q31_fmt(0.70710678118655));   /* Diff */
        xxr = *(pt_xr) << 1;
        xxl = *(pt_xl) << 1;
        *(pt_xr++)  = fxp_mul32_Q32((xxr + xxl), Q31_fmt(0.70710678118655));   /* Sum */
        *(pt_xl++)  = fxp_mul32_Q32((xxr - xxl), Q31_fmt(0.70710678118655));   /* Diff */
    }


    if (Number&1)
    {
        int32 xxr = *(pt_xr) << 1;
        int32 xxl = *(pt_xl) << 1;
        *(pt_xr)  = fxp_mul32_Q32((xxr + xxl), Q31_fmt(0.70710678118655));   /* Sum */
        *(pt_xl)  = fxp_mul32_Q32((xxr - xxl), Q31_fmt(0.70710678118655));   /* Diff */
    }

}


/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

// deliberately plays near overflow points of int32
#if __has_attribute(no_sanitize)
__attribute__((no_sanitize("integer")))
#endif
void pvmp3_st_intensity(int32 xr[SUBBANDS_NUMBER*FILTERBANK_BANDS],
                        int32 xl[SUBBANDS_NUMBER*FILTERBANK_BANDS],
                        int32 is_pos,
                        int32 Start,
                        int32 Number)
{

    int32 TmpFac = is_ratio_factor[ is_pos & 7];

    int32 *pt_xr  = &xr[Start];
    int32 *pt_xl  = &xl[Start];

    for (int32 i = Number >> 1; i != 0; i--)
    {
        int32 tmp = fxp_mul32_Q32((*pt_xr) << 1, TmpFac);
        *(pt_xl++) = (*pt_xr) - tmp;
        *(pt_xr++) = tmp;
        tmp = fxp_mul32_Q32((*pt_xr) << 1, TmpFac);
        *(pt_xl++) = (*pt_xr) - tmp;
        *(pt_xr++) = tmp;
    }

    if (Number&1)
    {
        int32 tmp = fxp_mul32_Q32((*pt_xr) << 1, TmpFac);
        *(pt_xl) = (*pt_xr) - tmp;
        *(pt_xr) = tmp;
    }

}

/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/
void pvmp3_stereo_proc(int32 xr[SUBBANDS_NUMBER*FILTERBANK_BANDS],
                       int32 xl[SUBBANDS_NUMBER*FILTERBANK_BANDS],
                       mp3ScaleFactors *scalefac,
                       granuleInfo *gr_info,
                       int32 used_freq_lines,
                       mp3Header *info)
{


    int32 sb;
    int32 ss;
    int32 sfbNo;
    int32 sfbStart;

    int32 sfb;
    int32 sfbTemp;
    int32 i;
    int32 j;


    int32 i_stereo  = (info->mode == MPG_MD_JOINT_STEREO) &&
                      (info->mode_ext & 0x1);

    int32 ms_stereo = (info->mode == MPG_MD_JOINT_STEREO) &&
                      (info->mode_ext & 0x2);

    int32 sfreq  = info->version_x + (info->version_x << 1);
    sfreq += info->sampling_frequency;




    if (i_stereo)
    {
        if (gr_info->window_switching_flag && (gr_info->block_type == 2))
        {
            if (gr_info->mixed_block_flag)
            {
                /*
                 * mixed blocks processing
                 */
                i = 31;
                ss = 17;
                sb = 0;
                while (i >= 0)
                {
                    if (xl[(i*FILTERBANK_BANDS) + ss])
                    {
                        sb = (i << 4) + (i << 1) + ss;
                        i = -1;
                    }
                    else
                    {
                        ss--;
                        if (ss < 0)
                        {
                            i--;
                            ss = 17;
                        }
                    }
                }

                if (sb < 36)
                {
                    /*
                     * mixed blocks processing: intensity bound inside long blocks
                     */
                    /* 1. long blocks up to intensity border: not intensity */

                    if (mp3_sfBandIndex[sfreq].l[4] <= sb)
                    {
                        sfb = 4;
                    }
                    else
                    {
                        sfb = 0;
                    }

                    while (mp3_sfBandIndex[sfreq].l[sfb] < sb)
                    {
                        sfb++;
                    }

                    /* from that sfb on intensity stereo */
                    sfbTemp = sfb;  /* save for later use */

                    sfbStart = mp3_sfBandIndex[sfreq].l[sfb];

                    /* from 0 up to sfbStart do ms_stereo or normal stereo */

                    if (ms_stereo)
                    {
                        pvmp3_st_mid_side(xr, xl, 0, sfbStart);
                    }

                    /* 2. long blocks from intensity border up to sfb band 8: intensity */
                    /* calc. is_ratio */


                    /* Start of intensity stereo of remaining sfc bands: */
                    for (; sfbTemp < 8; sfbTemp++)
                    {
                        sfbStart = mp3_sfBandIndex[sfreq].l[sfbTemp];  /* = Start in 0 ... 575 */
                        sfbNo = mp3_sfBandIndex[sfreq].l[sfbTemp+1] - mp3_sfBandIndex[sfreq].l[sfbTemp]; /* No of lines to process */

                        if (scalefac->l[sfbTemp] != 7)
                        {
                            pvmp3_st_intensity(xr, xl, scalefac->l[sfbTemp], sfbStart, sfbNo);
                        }
                        else if (ms_stereo)
                        {
                            pvmp3_st_mid_side(xr, xl, sfbStart, sfbNo);
                        }

                    }  /* for (; sfbTemp < 8; sfbTemp++) */

                    for (j = 0; j < 3; j++)
                    {
                        /* 3. short blocks from sfbcnt to last sfb do intensity stereo */
                        for (sfbTemp = 3; sfbTemp < 13; sfbTemp++)
                        {
                            sfbNo = mp3_sfBandIndex[sfreq].s[sfbTemp+1] - mp3_sfBandIndex[sfreq].s[sfbTemp]; /* No of lines to process */
                            sfbStart = 3 * mp3_sfBandIndex[sfreq].s[sfbTemp] + j * sfbNo;

                            if (scalefac->s[j][sfbTemp] != 7)
                            {
                                pvmp3_st_intensity(xr, xl, scalefac->s[j][sfbTemp], sfbStart, sfbNo);
                            }
                            else if (ms_stereo)
                            {
                                pvmp3_st_mid_side(xr, xl, sfbStart, sfbNo);
                            }

                        }  /* for (; sfbTemp < 22; sfbTemp++) */
                    } /* for (j = 0; j < 3; j++) */
                }
                else   /* else for (sb >= 36) */
                {
                    /*
                     * mixed blocks processing: intensity bound outside long blocks
                     */


                    /*
                     * 2. short blocks from sfb band 3 up to intensity border: normal stereo, ms stereo and intensity
                     */
                    for (j = 0; j < 3; j++)
                    {
                        int32 sfbcnt;
                        sfbcnt = -1;

                        for (sfb = 12; sfb >= 3; sfb--)
                        {
                            int32 lines;
                            lines = mp3_sfBandIndex[sfreq].s[sfb+1] - mp3_sfBandIndex[sfreq].s[sfb];
                            i = 3 * mp3_sfBandIndex[sfreq].s[sfb] + (j + 1) * lines - 1;

                            while (lines > 0)
                            {
                                if (xl[i])
                                {
                                    sfbcnt = sfb;
                                    sfb = -10;
                                    lines = -10;
                                }
                                lines--;
                                i--;
                            }
                        }

                        sfbcnt += 1;
                        if (sfbcnt < 3)
                        {
                            sfbcnt = 3;
                        }

                        sfbTemp = sfbcnt;        /* for later use */


                        /*
                         *   do normal stereo or MS stereo from sfb 3 to < sfbcnt:
                         */
                        for (sb = 3; sb < sfbcnt; sb++)
                        {
                            sfbNo = mp3_sfBandIndex[sfreq].s[sb+1] - mp3_sfBandIndex[sfreq].s[sb];
                            sfbStart = 3 * mp3_sfBandIndex[sfreq].s[sb] + j * sfbNo;

                            if (ms_stereo)
                            {
                                pvmp3_st_mid_side(xr, xl, sfbStart, sfbNo);
                            }

                        }

                        /* from sfbcnt to last sfb do intensity stereo */
                        for (; sfbTemp < 13; sfbTemp++)
                        {
                            sfbNo = mp3_sfBandIndex[sfreq].s[sfbTemp+1] - mp3_sfBandIndex[sfreq].s[sfbTemp]; /* No of lines to process */
                            sfbStart = 3 * mp3_sfBandIndex[sfreq].s[sfbTemp] + j * sfbNo;

                            if (scalefac->s[j][sfbTemp] != 7)
                            {
                                pvmp3_st_intensity(xr, xl, scalefac->s[j][sfbTemp], sfbStart, sfbNo);
                            }
                            else if (ms_stereo)
                            {
                                pvmp3_st_mid_side(xr, xl, sfbStart, sfbNo);
                            }

                        }  /* for (; sfbTemp < 22; sfbTemp++) */

                    } /* for (j = 0; j < 3; j++) */

                    /* 1. long blocks up to sfb band 8: not intensity */
                    /* from 0 to sfb 8 ms_stereo or normal stereo */

                    sfbStart = mp3_sfBandIndex[sfreq].l[8];

                    if (ms_stereo)
                    {
                        pvmp3_st_mid_side(xr, xl, 0, sfbStart);
                    }

                }
            }  /* if (gr_info->mixed_block_flag) */
            else
            {
                /*
                 * short block processing
                 */
                for (j = 0; j < 3; j++)
                {
                    int32 sfbcnt = -1;

                    for (sfb = 12; sfb >= 0; sfb--)
                    {
                        int32 lines = mp3_sfBandIndex[sfreq].s[sfb+1] - mp3_sfBandIndex[sfreq].s[sfb];
                        i = 3 * mp3_sfBandIndex[sfreq].s[sfb] + (j + 1) * lines - 1;

                        while (lines > 0)
                        {
                            if (xl[i])
                            {
                                sfbcnt = sfb;
                                sfb = -10;
                                lines = -10;
                            }
                            lines--;
                            i--;
                        }
                    }

                    sfbcnt += 1;
                    sfbTemp = sfbcnt;        /* for later use */

                    /* do normal stereo or MS stereo from 0 to sfbcnt */
                    for (sb = 0; sb < sfbcnt; sb++)
                    {
                        sfbNo = mp3_sfBandIndex[sfreq].s[sb+1] - mp3_sfBandIndex[sfreq].s[sb];
                        sfbStart = 3 * mp3_sfBandIndex[sfreq].s[sb] + j * sfbNo;

                        if (ms_stereo)
                        {
                            pvmp3_st_mid_side(xr, xl, sfbStart, sfbNo);
                        }
                    }


                    /* from sfbcnt to last sfb do intensity stereo */
                    for (; sfbTemp < 13; sfbTemp++)
                    {
                        sfbNo = mp3_sfBandIndex[sfreq].s[sfbTemp+1] - mp3_sfBandIndex[sfreq].s[sfbTemp]; /* No of lines to process */
                        sfbStart = 3 * mp3_sfBandIndex[sfreq].s[sfbTemp] + j * sfbNo;

                        if (scalefac->s[j][sfbTemp] != 7)
                        {
                            pvmp3_st_intensity(xr, xl, scalefac->s[j][sfbTemp], sfbStart, sfbNo);
                        }
                        else if (ms_stereo)
                        {
                            pvmp3_st_mid_side(xr, xl, sfbStart, sfbNo);
                        }

                    }  /* for (; sfbTemp < 22; sfbTemp++) */

                } /* for (j = 0; j < 3; j++) */

            } /* if( gr_info->mixed_block_flag) */



        }  /* if (gr_info->window_switching_flag && (gr_info->block_type == 2)) */
        else
        {
            /*
             *   long block processing
             */
            i = 31;
            ss = 17;
            sb = 0;

            while (i >= 0)
            {
                if (xl[(i*FILTERBANK_BANDS) + ss] != 0)
                {
                    sb = (i << 4) + (i << 1) + ss;
                    i = -2;
                }
                else
                {
                    ss--;
                    if (ss < 0)
                    {
                        i--;
                        ss = 17;
                    }
                }
            }

            if (sb)
            {
                if (mp3_sfBandIndex[sfreq].l[14] <= sb)
                {
                    sfb = 14;
                }
                else if (mp3_sfBandIndex[sfreq].l[7] <= sb)
                {
                    sfb = 7;
                }
                else
                {
                    sfb = 0;
                }


                while (mp3_sfBandIndex[sfreq].l[sfb] <= sb)
                {
                    sfb++;
                }
            }
            else
            {
                if (i == -1)
                {
                    /*  all xr[1][][] are 0: set IS bound sfb to 0  */
                    sfb = 0;
                }
                else
                {
                    /*  xr[1][0][0] is unequal 0 and all others are 0: set IS bound sfb to 1 */
                    sfb = 1;
                }
            }

            sfbTemp = sfb;  /* save for later use */


            sfbStart = mp3_sfBandIndex[sfreq].l[sfb];

            /* from 0 to sfbStart ms_stereo or normal stereo */
            if (ms_stereo)
            {
                pvmp3_st_mid_side(xr, xl, 0, sfbStart);
            }

            /* now intensity stereo of the remaining sfb's: */
            for (; sfb < 21; sfb++)
            {
                sfbStart = mp3_sfBandIndex[sfreq].l[sfb];
                sfbNo = mp3_sfBandIndex[sfreq].l[sfb+1] - mp3_sfBandIndex[sfreq].l[sfb]; /* No of lines to process */

                if (scalefac->l[sfb] != 7)
                {
                    pvmp3_st_intensity(xr, xl, scalefac->l[sfb], sfbStart, sfbNo);
                }
                else if (ms_stereo)
                {
                    pvmp3_st_mid_side(xr, xl, sfbStart, sfbNo);
                }

            }  /* for (; sfbTemp < 22; sfbTemp++) */



            sfbStart = mp3_sfBandIndex[sfreq].l[21];
            sfbNo = mp3_sfBandIndex[sfreq].l[22] - mp3_sfBandIndex[sfreq].l[21]; /* No of lines to process */

            if (scalefac->l[21] != 7)
            {
                if (sfbTemp < 21)
                {
                    sfbTemp = scalefac->l[20];
                }
                else
                {
                    sfbTemp = 0;  /* if scalefac[20] is not an intensity position, is_pos = 0 */
                }

                pvmp3_st_intensity(xr, xl, sfbTemp, sfbStart, sfbNo);
            }
            else if (ms_stereo)
            {
                pvmp3_st_mid_side(xr, xl, sfbStart, sfbNo);
            }

        }  /* if (gr_info->window_switching_flag && (gr_info->block_type == 2)) */


    }  /* if (i_stereo)  */
    else
    {
        /*
         * normal or ms stereo processing
         */
        if (ms_stereo)
        {

            pvmp3_st_mid_side(xr, xl, 0, used_freq_lines);

        }

    } /* if (i_stereo) */

}

